Skip to content

test: split attachments.test.js#492

Draft
stefanrudi wants to merge 1 commit into
mainfrom
ci/cleanup-tests
Draft

test: split attachments.test.js#492
stefanrudi wants to merge 1 commit into
mainfrom
ci/cleanup-tests

Conversation

@stefanrudi

@stefanrudi stefanrudi commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
  • Split the test file attachments.test.js to leverage parallel execution

@hyperspace-pr-bot hyperspace-pr-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR splits a large attachments.test.js into focused files, which is a good structural improvement. Several correctness issues were found: silent test assertions inside .catch() blocks that never execute on 2xx responses (making tests always pass), a double-await pattern copied across two files, a wrong EICAR-like mime type in an error message assertion, a misleading comment that contradicts its assertion, and a magic -1 sentinel value in the shared utility function that could produce a malformed Content-Length header if misused.

PR Bot Information

Version: 1.29.2

  • LLM: anthropic--claude-4.6-sonnet
  • Correlation ID: 328db820-8620-11f1-95b2-b3e78fa5cb02
  • Experiment Variant: control
  • Event Trigger: pull_request.opened
  • File Content Strategy: Full file content

const incidentID = await newIncident(POST, "processor")
let sampleDocID

const scanCleanWaiter = waitForScanStatus("Clean")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: scanCleanWaiter is created before the attachment is uploaded, but the test never awaits it

scanCleanWaiter is declared and the waiter promise is created at line 351 before the upload, but the test never calls await scanCleanWaiter. Without this, the test proceeds to read content without waiting for the scan to complete. If the scan hasn't finished by the time the content GET happens, the test may fail non-deterministically or skip verifying the scan-clean state.

Consider adding await scanCleanWaiter after the GET at line 360 or before the content read, consistent with how other tests in this file use the waiter.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

Comment on lines +275 to +282
await GET(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content`,
).catch((e) => {
expect(e.status).toEqual(202)
expect(e.response.data.error.message).toContain(
"The previous scan was more than 3 days ago. Please try to download again in a moment, after the attachment is rescanned.",
)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: GET on a 202 response silently passes — test assertion is only inside .catch()

The .catch() block at line 277 expects e.status === 202, but a 202 is a successful HTTP status. The GET call will resolve (not reject) with status 202, so the .catch() callback is never invoked and the assertions are silently skipped. The test always passes regardless of what the server returns.

Consider using await GET(...).then(res => { expect(res.status).toEqual(202); ... }) or checking if axios throws for 2xx responses, or wrapping with a flag to ensure the assertion was reached:

const rescanRes = await GET(
  `odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content`,
).catch((e) => e.response)
expect(rescanRes.status).toEqual(202)
expect(rescanRes.data.error.message).toContain(
  "The previous scan was more than 3 days ago. Please try to download again in a moment, after the attachment is rescanned.",
)
Suggested change
await GET(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content`,
).catch((e) => {
expect(e.status).toEqual(202)
expect(e.response.data.error.message).toContain(
"The previous scan was more than 3 days ago. Please try to download again in a moment, after the attachment is rescanned.",
)
})
const rescanRes = await GET(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content`,
).catch((e) => e.response)
expect(rescanRes.status).toEqual(202)
expect(rescanRes.data.error.message).toContain(
"The previous scan was more than 3 days ago. Please try to download again in a moment, after the attachment is rescanned.",
)

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

Comment on lines +414 to +419
await GET(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=true)/attachments(up__ID=${incidentID},ID=${sampleDocID},IsActiveEntity=true)/content`,
).catch((e) => {
expect(e.status).toEqual(404)
expect(e.response.data.error.message).toMatch(/Not Found/)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: GET on a 404 response is asserted only inside .catch(), but may silently pass

The .catch() at line 416 only runs if the request throws. If the server returns 404 as a resolved response (some HTTP clients don't throw on 4xx), the assertions at lines 417–418 are never reached and the test silently passes without any verification. The same pattern exists at lines 437–440.

Consider using a boolean flag to guarantee the error handler was called:

let didThrow = false
await GET(...).catch((e) => {
  didThrow = true
  expect(e.status).toEqual(404)
  expect(e.response.data.error.message).toMatch(/Not Found/)
})
expect(didThrow).toBe(true)

Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

up__ID: testID,
filename: "testfile.pdf",
mimeType: "application/pdf",
createdAt: new Date(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: await await is a redundant double-await

await await runWithUser(...) at line 830 double-awaits the same promise. The inner await resolves the promise returned by runWithUser, and the outer await then awaits a non-Promise value, making the second await a no-op. This is identical to the same pattern at line 904 in attachments-rename.test.js. While harmless at runtime, it's misleading and may indicate a copy-paste oversight.

Should be const newAtt = await runWithUser(alice, () => ...).

Suggested change
createdAt: new Date(),
const newAtt = await runWithUser(alice, () =>
AttachmentsSrv.copy(
Attachments,
{ ID: sourceAttachmentID },
Attachments.drafts,
{
up__ID: targetIncidentID,
DraftAdministrativeData_DraftUUID:
targetDraft.DraftAdministrativeData_DraftUUID,
},
),
)

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/attachments`,
)
//the data should have no attachments
expect(response.status).toEqual(200)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: Comment says "data should have no attachments" but expects exactly one

The inline comment at line 632 says //the data should have no attachments, but the assertion on line 634 expects response.data.value.length to equal 1. The comment is misleading and inverts the actual intent of the test.

Should be updated to //the data should have one attachment.

Suggested change
expect(response.status).toEqual(200)
//the data should have one attachment

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful


//content should not be there
const responseContent = await GET(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/attachments(up__ID=${incidentID},ID=${attachmentsID},IsActiveEntity=true)/content`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: The IsActiveEntity=true in the URL is wrong for a draft (false) context

At line 640, the GET request fetches attachment content using IsActiveEntity=true for what the test intends to be a draft-mode attachment (uploaded via INSERT.into(...drafts)). This likely causes the test to pass accidentally (or fail for the wrong reason) since the attachment was inserted into the draft table and would not yet be active. The path should use IsActiveEntity=false to match the draft-only attachment that was inserted.

Suggested change
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/attachments(up__ID=${incidentID},ID=${attachmentsID},IsActiveEntity=true)/content`,
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/attachments(up__ID=${incidentID},ID=${attachmentsID},IsActiveEntity=false)/content`,

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

try {
await waitForDeletion(attachmentResponse.data.value[0].url)
// Should throw due to timeout
expect(true).toEqual(false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: expect(true).toEqual(false) is not a meaningful assertion for "should have thrown"

At line 1938, the test intends to fail if waitForDeletion resolves without throwing. Using expect(true).toEqual(false) technically works but is opaque. A more idiomatic and readable approach is fail("waitForDeletion should have timed out but resolved instead") or throw new Error("Expected timeout but resolved"). Additionally, the comment at line 1937 ("Should throw due to timeout") should be phrased as "waitForDeletion should have rejected with a Timeout error" for clarity.

Suggested change
expect(true).toEqual(false)
throw new Error("Expected waitForDeletion to time out, but it resolved")

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

Comment on lines +216 to +232
await POST(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/mediaTypeAttachments`,
{
up__ID: incidentID,
filename: "sample.pdf",
mimeType: "application/jpeg charset=UTF-8",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
).catch((e) => {
expect(e.status).toEqual(400)
expect(e.response.data.error.message).toMatch(
"The attachment file type 'application/pdf' is not allowed.",
)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: Wrong error message matched in .catch() — tests 'application/pdf' but mimeType sent is 'application/jpeg charset=UTF-8'

In the .catch() at lines 228–232, the expected error message checks for "The attachment file type 'application/pdf' is not allowed.", but the mimeType field sent in the POST body is "application/jpeg charset=UTF-8". The assertion will never match because the server would report the rejected mime type as application/jpeg, not application/pdf. This means any error thrown here (whether for the right or wrong reason) would fail the inner assertion, but because it's inside .catch() without a guarantee the callback runs, the test may silently pass.

The expected message should reference 'application/jpeg'.

Suggested change
await POST(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/mediaTypeAttachments`,
{
up__ID: incidentID,
filename: "sample.pdf",
mimeType: "application/jpeg charset=UTF-8",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
).catch((e) => {
expect(e.status).toEqual(400)
expect(e.response.data.error.message).toMatch(
"The attachment file type 'application/pdf' is not allowed.",
)
})
await POST(
`odata/v4/processor/Incidents(ID=${incidentID},IsActiveEntity=false)/mediaTypeAttachments`,
{
up__ID: incidentID,
filename: "sample.pdf",
mimeType: "application/jpeg charset=UTF-8",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
).catch((e) => {
expect(e.status).toEqual(400)
expect(e.response.data.error.message).toMatch(
"The attachment file type 'application/jpeg' is not allowed.",
)
})

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

expect(targetDraft?.DraftAdministrativeData_DraftUUID).toBeTruthy()

// Source is the active Attachments entity (uploaded via draft, now active after save)
const newAtt = await await runWithUser(alice, () =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: await await runWithUser(...) — redundant double-await

At line 904, const newAtt = await await runWithUser(...) double-awaits the same promise. The inner await already resolves the Promise returned by runWithUser; the outer await receives a plain value and is a no-op. Same issue exists in attachments-draft.test.js line 830.

Should be const newAtt = await runWithUser(alice, () => ...).

Suggested change
const newAtt = await await runWithUser(alice, () =>
const newAtt = await runWithUser(alice, () =>
AttachmentsSrv.copy(
Attachments,
{ ID: sourceAttachmentID },
Attachments.drafts,
{
up__ID: targetIncidentID,
DraftAdministrativeData_DraftUUID:
targetDraft.DraftAdministrativeData_DraftUUID,
},
),
)

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

Comment thread tests/utils/testUtils.js
Comment on lines +203 to +235
overrideContentLength = -1,
entityName = "attachments",
) {
const filepath = join(__dirname, "..", "integration", "content/sample.pdf")

await utils.draftModeEdit(
"processor",
"Incidents",
incidentId,
"ProcessorService",
)

const res = await POST(
`odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)/${entityName}`,
{
up__ID: incidentId,
filename: basename(filepath),
mimeType: "application/pdf",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
)
const fileContent = readFileSync(filepath)
await PUT(
`/odata/v4/processor/Incidents_${entityName}(up__ID=${incidentId},ID=${res.data.ID},IsActiveEntity=false)/content`,
fileContent,
{
headers: {
"Content-Type": "application/pdf",
"Content-Length":
overrideContentLength != -1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: Sentinel value -1 used for "use default" is semantically unclear and fragile

The overrideContentLength parameter uses -1 as a sentinel to mean "use the actual file size". This makes the JSDoc misleading (-1 = use file size), requires callers to remember the magic value, and could silently produce an incorrect Content-Length: -1 header if a caller accidentally passes 0 instead of -1.

Consider using null or undefined as the sentinel value for "not overridden", which is more idiomatic in JavaScript:

async function uploadDraftAttachment(
  utils, POST, PUT, GET, incidentId,
  overrideContentLength = null,
  entityName = "attachments",
) {
  ...
  "Content-Length": overrideContentLength ?? fileContent.byteLength,
Suggested change
overrideContentLength = -1,
entityName = "attachments",
) {
const filepath = join(__dirname, "..", "integration", "content/sample.pdf")
await utils.draftModeEdit(
"processor",
"Incidents",
incidentId,
"ProcessorService",
)
const res = await POST(
`odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)/${entityName}`,
{
up__ID: incidentId,
filename: basename(filepath),
mimeType: "application/pdf",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
)
const fileContent = readFileSync(filepath)
await PUT(
`/odata/v4/processor/Incidents_${entityName}(up__ID=${incidentId},ID=${res.data.ID},IsActiveEntity=false)/content`,
fileContent,
{
headers: {
"Content-Type": "application/pdf",
"Content-Length":
overrideContentLength != -1
async function uploadDraftAttachment(
utils,
POST,
PUT,
GET,
incidentId,
overrideContentLength = null,
entityName = "attachments",
) {
const filepath = join(__dirname, "..", "integration", "content/sample.pdf")
await utils.draftModeEdit(
"processor",
"Incidents",
incidentId,
"ProcessorService",
)
const res = await POST(
`odata/v4/processor/Incidents(ID=${incidentId},IsActiveEntity=false)/${entityName}`,
{
up__ID: incidentId,
filename: basename(filepath),
mimeType: "application/pdf",
createdAt: new Date(
Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000,
),
createdBy: "alice",
},
)
const fileContent = readFileSync(filepath)
await PUT(
`/odata/v4/processor/Incidents_${entityName}(up__ID=${incidentId},ID=${res.data.ID},IsActiveEntity=false)/content`,
fileContent,
{
headers: {
"Content-Type": "application/pdf",
"Content-Length": overrideContentLength ?? fileContent.byteLength,
},
},
)

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • ✅ Helpful comment
  • 🤷 Neutral
  • ❌ This comment is not helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant