diff --git a/dotcms-postman/src/main/resources/postman/ContentImportResource.postman_collection.json b/dotcms-postman/src/main/resources/postman/ContentImportResource.postman_collection.json index 2aaf65386e52..ea98a5068669 100644 --- a/dotcms-postman/src/main/resources/postman/ContentImportResource.postman_collection.json +++ b/dotcms-postman/src/main/resources/postman/ContentImportResource.postman_collection.json @@ -2226,12 +2226,11 @@ "});", "", "", + "// States returned by PostgresJobQueue.getActiveJobs", + "const ACTIVE_STATES = [\"PENDING\", \"RUNNING\", \"FAILED\", \"ABANDONED\", \"CANCEL_REQUESTED\", \"CANCELLING\"];", + "", "// Iterate over each job in the 'jobs' array", "entity.jobs.forEach((job, index) => {", - " pm.test(`Job ${index + 1}: 'completedAt' is not present`, function () {", - " pm.expect(job).to.not.have.property('completedAt')", - " });", - "", " pm.test(`Job ${index + 1}: 'createdAt' is a valid date string`, function () {", " pm.expect(job.createdAt).to.be.a(\"string\");", " pm.expect(new Date(job.createdAt)).to.not.equal(\"Invalid Date\");", @@ -2274,12 +2273,33 @@ " pm.expect(job.retryCount).to.be.a(\"number\").that.is.at.least(0);", " });", "", - " pm.test(`Job ${index + 1}: 'startedAt' is not present`, function () {", - " pm.expect(job).to.not.have.property('startedAt')", + " // The job processor runs concurrently with this request, so a job can move from PENDING to", + " // RUNNING (or beyond) at any moment. Timestamps are asserted against the state the server", + " // reports for each job, never against when the request happened to land.", + " pm.test(`Job ${index + 1}: 'state' is one of the active states`, function () {", + " pm.expect(job.state).to.be.oneOf(ACTIVE_STATES);", " });", "", - " pm.test(`Job ${index + 1}: 'state' is a non-empty string`, function () {", - " pm.expect(job.state).to.be.a(\"string\").that.is.not.empty;", + " pm.test(`Job ${index + 1}: 'startedAt' and 'completedAt' match state ${job.state}`, function () {", + " const isValidDate = (val) => !isNaN(new Date(val).getTime());", + " if (job.state === \"PENDING\") {", + " pm.expect(job).to.not.have.property('startedAt');", + " pm.expect(job).to.not.have.property('completedAt');", + " }", + " if (job.state === \"RUNNING\") {", + " pm.expect(job).to.have.property('startedAt');", + " pm.expect(job).to.not.have.property('completedAt');", + " }", + " if (job.state === \"FAILED\" || job.state === \"ABANDONED\") {", + " pm.expect(job).to.have.property('completedAt');", + " }", + " // CANCEL_REQUESTED and CANCELLING can apply to a job that never started, so their", + " // timestamps are optional and only checked for validity when present.", + " ['startedAt', 'completedAt'].forEach((field) => {", + " if (job[field] !== undefined && job[field] !== null) {", + " pm.expect(isValidDate(job[field]), `${field} is a valid date`).to.be.true;", + " }", + " });", " });", "", " pm.test(`Job ${index + 1}: 'updatedAt' is a valid date string`, function () {", @@ -2293,11 +2313,26 @@ "", "var jobId = pm.collectionVariables.get(\"jobId\");", "", - "pm.test(\"jobId is present in the response\", function () {", + "pm.test(\"jobId is present in the response or already finished successfully\", function () {", " var jobFound = jobsArray.some(function(job) {", " return job.id === jobId;", " });", - " pm.expect(jobFound).to.be.true;", + " if (jobFound) {", + " return;", + " }", + " // A small import can finish before this request lands, which removes it from the active", + " // list. In that case the job must have completed successfully.", + " pm.sendRequest({", + " url: (pm.environment.get(\"serverURL\") || pm.collectionVariables.get(\"baseUrl\")) + \"/api/v1/content/_import/\" + jobId,", + " method: \"GET\",", + " header: { \"Authorization\": \"Bearer \" + pm.variables.get(\"jwt\") }", + " }, function (err, res) {", + " pm.test(\"jobId missing from the active list has finished successfully\", function () {", + " pm.expect(err).to.be.null;", + " pm.expect(res.code, \"job lookup returned \" + res.code).to.equal(200);", + " pm.expect(res.json().entity.state).to.equal(\"SUCCESS\");", + " });", + " });", "});", "", "// Validate pagination fields within `entity`",