[ui, tests] Various acceptance test fixups (v main) (#25031)

* Add factory hooks for jobs to have previously stable versions and stopped status

* Since #24973 node-read isn't presupposed and so should regex match only on the common url parts

* Job detail tests for title buttons are now bimodal and default to having previously-stable version in history

* prettier plz

* Breaking a thing on purpose to see if my other broken thing is broken

* continue-on-error set to false to get things red when appropriate

* OK what if continue-on-error=true but we do a separate failure reporting after the fact

* fail-fast are you the magic incantation that I need?

* Re-fix my test now that fast-fail is off

* Fix to server-leader by adding a region first, and always()-append to uploading partition results

* Express failure step lists failing tests so you don't have to click back into ember-exam step

* temporary snapshot and logging for flakey test in service job detail

* Bunch of region and tasklogs test fixups

* using allocStatusDistribution to ensure service job always has a non-queued alloc
This commit is contained in:
Phil Renaud
2025-02-20 16:56:14 -05:00
committed by GitHub
parent d63c1d0bad
commit 3f764a21bc
10 changed files with 106 additions and 20 deletions

View File

@@ -175,6 +175,9 @@ export default Factory.extend({
// When true, the job will have no versions or deployments (and in turn no latest deployment)
noDeployments: false,
// When true, the job will have a previous stable version. Useful for testing "start job" loop.
withPreviousStableVersion: false,
// When true, an evaluation with a high modify index and placement failures is created
failedPlacements: false,
@@ -317,8 +320,20 @@ export default Factory.extend({
version: index,
noActiveDeployment: job.noActiveDeployment,
activeDeployment: job.activeDeployment,
stable: true,
});
});
if (job.withPreviousStableVersion) {
server.create('job-version', {
job,
namespace: job.namespace,
version: 1,
noActiveDeployment: job.noActiveDeployment,
activeDeployment: job.activeDeployment,
stable: true,
});
}
}
if (job.activeDeployment) {

View File

@@ -27,6 +27,7 @@ moduleForJob('Acceptance | job detail (batch)', 'allocations', () =>
allocStatusDistribution: {
running: 1,
},
withPreviousStableVersion: true,
})
);
@@ -39,6 +40,7 @@ moduleForJob('Acceptance | job detail (system)', 'allocations', () =>
allocStatusDistribution: {
running: 1,
},
withPreviousStableVersion: true,
})
);
@@ -52,6 +54,7 @@ moduleForJob('Acceptance | job detail (sysbatch)', 'allocations', () =>
running: 1,
failed: 1,
},
withPreviousStableVersion: true,
})
);
@@ -65,6 +68,7 @@ moduleForJobWithClientStatus(
type: 'sysbatch',
createAllocations: false,
noActiveDeployment: true,
withPreviousStableVersion: true,
});
}
);
@@ -80,6 +84,7 @@ moduleForJobWithClientStatus(
namespaceId: namespace.name,
createAllocations: false,
noActiveDeployment: true,
withPreviousStableVersion: true,
});
}
);
@@ -95,6 +100,7 @@ moduleForJobWithClientStatus(
namespaceId: namespace.name,
createAllocations: false,
noActiveDeployment: true,
withPreviousStableVersion: true,
});
}
);
@@ -109,6 +115,7 @@ moduleForJob('Acceptance | job detail (sysbatch child)', 'allocations', () => {
running: 1,
},
noActiveDeployment: true,
withPreviousStableVersion: true,
});
return server.db.jobs.where({ parentId: parent.id })[0];
});
@@ -212,6 +219,7 @@ moduleForJob(
server.create('job', 'parameterized', {
shallow: true,
noActiveDeployment: true,
withPreviousStableVersion: true,
}),
{
'the default sort is submitTime descending': async (job, assert) => {
@@ -292,7 +300,15 @@ moduleForJob(
moduleForJob(
'Acceptance | job detail (service)',
'allocations',
() => server.create('job', { type: 'service', noActiveDeployment: true }),
() =>
server.create('job', {
type: 'service',
noActiveDeployment: true,
withPreviousStableVersion: true,
allocStatusDistribution: {
running: 1,
},
}),
{
'the subnav links to deployment': async (job, assert) => {
await JobDetail.tabFor('deployments').visit();

View File

@@ -5,7 +5,7 @@
/* eslint-disable qunit/require-expect */
/* eslint-disable qunit/no-conditional-assertions */
import { currentURL } from '@ember/test-helpers';
import { currentURL, settled } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { selectChoose } from 'ember-power-select/test-support';
@@ -80,9 +80,11 @@ module('Acceptance | regions (only one)', function (hooks) {
await JobsList.jobs.objectAt(0).clickRow();
await Layout.gutter.visitClients();
await Layout.gutter.visitServers();
server.pretender.handledRequests.forEach((req) => {
assert.notOk(req.url.includes('region='), req.url);
});
server.pretender.handledRequests
.filter((req) => !req.url.includes('/v1/status/leader'))
.forEach((req) => {
assert.notOk(req.url.includes('region='), req.url);
});
});
});
@@ -114,7 +116,10 @@ module('Acceptance | regions (many)', function (hooks) {
});
test('when on the default region, pages do not include the region query param', async function (assert) {
let managementToken = server.create('token');
window.localStorage.nomadTokenSecret = managementToken.secretId;
await JobsList.visit();
await settled();
assert.equal(currentURL(), '/jobs', 'No region query param');
assert.equal(
@@ -143,11 +148,13 @@ module('Acceptance | regions (many)', function (hooks) {
});
test('switching regions to the default region, unsets the region query param', async function (assert) {
let managementToken = server.create('token');
window.localStorage.nomadTokenSecret = managementToken.secretId;
const startingRegion = server.db.regions[1].id;
const defaultRegion = server.db.regions[0].id;
await JobsList.visit({ region: startingRegion });
await settled();
await selectChoose('[data-test-region-switcher-parent]', defaultRegion);
assert.notOk(
@@ -197,7 +204,8 @@ module('Acceptance | regions (many)', function (hooks) {
const appRequests = server.pretender.handledRequests.filter(
(req) =>
!req.responseURL.includes('/v1/regions') &&
!req.responseURL.includes('/v1/operator/license')
!req.responseURL.includes('/v1/operator/license') &&
!req.responseURL.includes('/v1/status/leader')
);
assert.notOk(

View File

@@ -20,6 +20,7 @@ module('Acceptance | server detail', function (hooks) {
hooks.beforeEach(async function () {
server.createList('agent', 3);
server.create('region', { id: 'global' });
agent = server.db.agents[0];
await ServerDetail.visit({ name: agent.name });
});

View File

@@ -62,10 +62,7 @@ module('Acceptance | task logs', function (hooks) {
test('the stdout log immediately starts streaming', async function (assert) {
await TaskLogs.visit({ id: allocation.id, name: task.name });
const node = server.db.nodes.find(allocation.nodeId);
const logUrlRegex = new RegExp(
`${node.httpAddr}/v1/client/fs/logs/${allocation.id}`
);
const logUrlRegex = new RegExp(`/v1/client/fs/logs/${allocation.id}`);
assert.ok(
server.pretender.handledRequests.filter((req) =>
logUrlRegex.test(req.url)

View File

@@ -92,7 +92,11 @@ export default function moduleForJob(
test('the title buttons are dependent on job status', async function (assert) {
if (job.status === 'dead') {
assert.ok(JobDetail.start.isPresent);
if (job.stopped) {
assert.ok(JobDetail.start.isPresent);
} else {
assert.ok(JobDetail.revert.isPresent);
}
assert.ok(JobDetail.purge.isPresent);
assert.notOk(JobDetail.stop.isPresent);
assert.notOk(JobDetail.execButton.isPresent);

View File

@@ -202,6 +202,8 @@ module('Integration | Component | job-page/periodic', function (hooks) {
childrenCount: 0,
createAllocations: false,
status: 'dead',
withPreviousStableVersion: true,
stopped: true,
});
await this.store.findAll('job');
@@ -223,6 +225,8 @@ module('Integration | Component | job-page/periodic', function (hooks) {
childrenCount: 0,
createAllocations: false,
status: 'dead',
withPreviousStableVersion: true,
stopped: true,
});
await this.store.findAll('job');
@@ -243,6 +247,8 @@ module('Integration | Component | job-page/periodic', function (hooks) {
childrenCount: 0,
createAllocations: false,
status: 'dead',
withPreviousStableVersion: true,
stopped: true,
});
await this.store.findAll('job');

View File

@@ -112,7 +112,11 @@ module('Integration | Component | job-page/service', function (hooks) {
test('Starting a job sends a post request for the job using the current definition', async function (assert) {
assert.expect(1);
const mirageJob = makeMirageJob(this.server, { status: 'dead' });
const mirageJob = makeMirageJob(this.server, {
status: 'dead',
withPreviousStableVersion: true,
stopped: true,
});
await this.store.findAll('job');
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
@@ -129,7 +133,11 @@ module('Integration | Component | job-page/service', function (hooks) {
this.server.pretender.post('/v1/job/:id', () => [403, {}, '']);
const mirageJob = makeMirageJob(this.server, { status: 'dead' });
const mirageJob = makeMirageJob(this.server, {
status: 'dead',
withPreviousStableVersion: true,
stopped: true,
});
await this.store.findAll('job');
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);
@@ -144,7 +152,10 @@ module('Integration | Component | job-page/service', function (hooks) {
test('Purging a job sends a purge request for the job', async function (assert) {
assert.expect(1);
const mirageJob = makeMirageJob(this.server, { status: 'dead' });
const mirageJob = makeMirageJob(this.server, {
status: 'dead',
withPreviousStableVersion: true,
});
await this.store.findAll('job');
const job = this.store.peekAll('job').findBy('plainId', mirageJob.id);

View File

@@ -11,6 +11,7 @@ import hbs from 'htmlbars-inline-precompile';
import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit';
import Pretender from 'pretender';
import { logEncode } from '../../../mirage/data/logs';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
const HOST = '1.1.1.1:1111';
const allowedConnectionTime = 100;
@@ -36,7 +37,22 @@ let logMode = null;
module('Integration | Component | task log', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
hooks.beforeEach(async function () {
this.server = startMirage();
const managementToken = this.server.create('token');
window.localStorage.nomadTokenSecret = managementToken.secretId;
const tokenService = this.owner.lookup('service:token');
const tokenPromise = tokenService.fetchSelfTokenAndPolicies.perform();
const timeoutPromise = new Promise((_, reject) => {
setTimeout(
() => reject(new Error('Token fetch timed out after 3 seconds')),
3000
);
});
await Promise.race([tokenPromise, timeoutPromise]);
// ^--- TODO: noticed some flakiness in local testing; this is meant to suss it out in CI.
await settled();
const handler = ({ queryParams }) => {
let frames;
let data;