Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4df89d3
Modernize testing: migrate from Jest to node:test
cubap Apr 13, 2026
7ac283a
Add Playwright e2e smoke tests and CI workflow
cubap Apr 13, 2026
091234b
coverage thresholds
cubap Apr 13, 2026
8424ea2
e2e(ui): add browser helper and new smoke tests
cubap Apr 13, 2026
e1f155d
Update tokens.js
cubap Apr 13, 2026
7ad9e49
Update .github/workflows/tests.yaml
cubap Apr 13, 2026
adb1916
Update package.json
cubap Apr 13, 2026
b749933
better test description
cubap Apr 14, 2026
f6ab638
Validate upstream RERUM contract in tests
cubap Apr 14, 2026
75d10c2
overwrite header inclusion
cubap Apr 14, 2026
37f15ab
Accept application/ld+json in tests; add 415/502 cases
cubap Apr 14, 2026
e9d0904
Update TESTING.md
cubap Apr 16, 2026
92cef9d
test description cleanup
cubap Apr 16, 2026
2204f9b
Easy gap to cover. Cleanup dead index.js code.
thehabes Apr 16, 2026
f30cc4b
Merge branch 'main' into pr/115
cubap Apr 16, 2026
fc689d5
Create app-cors.test.js
cubap Apr 16, 2026
fd2fa6f
Add create id-mapping tests and content-type tests
cubap Apr 16, 2026
8ab1918
Add tests for messenger headers and text body
cubap Apr 16, 2026
5c11a71
Add test for fetchRerum timeout mapping
cubap Apr 16, 2026
532a9ff
Add token tests; extend error & fetchRerum tests
cubap Apr 16, 2026
4fa7521
Update update.test.js
cubap Apr 16, 2026
c585a14
network query failures
cubap Apr 16, 2026
a494b71
nextwork errros
cubap Apr 16, 2026
28b95eb
more tests for file env token refresh
cubap Apr 16, 2026
6f44526
Update index.test.js
cubap Apr 16, 2026
af47f07
Add error-handling and token unit tests
cubap Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions test/routes/app-cors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import assert from "node:assert/strict"
import { afterEach, beforeEach, describe, it } from "node:test"
import request from "supertest"

const originalOpenApiCors = process.env.OPEN_API_CORS

beforeEach(() => {
process.env.OPEN_API_CORS = "true"
})

afterEach(() => {
process.env.OPEN_API_CORS = originalOpenApiCors
})

describe("App CORS middleware behavior. __core", () => {
it("Adds CORS headers when OPEN_API_CORS is enabled.", async () => {
const { default: app } = await import("../../app.js?test-cors-enabled")

const response = await request(app)
.get("/index.html")
.set("Origin", "http://example.test")

assert.equal(response.statusCode, 200)
assert.equal(response.header["access-control-allow-origin"], "*")
assert.equal(response.header["access-control-expose-headers"], "*")
})
})
45 changes: 45 additions & 0 deletions test/routes/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ describe("Check that the request/response behavior of the TinyNode create route
assert.equal(lastFetchOptions.headers["Content-Type"], "application/json;charset=utf-8", "Content-Type header mismatch")
})

it("Converts body id to _id before sending upstream.", async () => {
const response = await request(routeTester)
.post("/create")
.send({ id: "https://example.org/id/abc123", test: "item" })
.set("Content-Type", "application/json")

assert.equal(response.statusCode, 201)
const upstreamBody = JSON.parse(lastFetchOptions.body)
assert.equal(upstreamBody._id, "abc123")
assert.equal(upstreamBody.id, "https://example.org/id/abc123")
})

it("Accepts application/ld+json content type.", async () => {
const response = await request(routeTester)
.post("/create")
Expand Down Expand Up @@ -139,6 +151,39 @@ describe("Check that TinyNode create route propagates upstream and network error
assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})

it("Falls back to generic RERUM error text when upstream .text() throws.", async () => {
global.fetch = async () => ({
ok: false,
status: 500,
text: async () => {
throw new Error("text stream consumed")
}
})

const response = await request(routeTester)
.post("/create")
.set("Content-Type", "application/json")
.send({ test: "item" })

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})

it("Maps successful upstream payload without id fields to 502.", async () => {
global.fetch = async () => ({
ok: true,
json: async () => ({ test: "item" })
})

const response = await request(routeTester)
.post("/create")
.set("Content-Type", "application/json")
.send({ test: "item" })

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})
})

describe("Check that the properly used create endpoints function and interact with RERUM. __e2e", () => {
Expand Down
64 changes: 64 additions & 0 deletions test/routes/delete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,68 @@ describe("Delete network failure and passthrough behavior. __rest __core", () =
.delete("/delete/00000")
assert.equal(response.statusCode, 502)
})

it("Preserves upstream text error message for body delete when response is non-ok.", async () => {
global.fetch = async () => ({
ok: false,
status: 503,
text: async () => "Upstream body delete failure"
})

const response = await request(routeTester)
.delete("/delete")
.set("Content-Type", "application/json")
.send({ "@id": rerumUri })

assert.equal(response.statusCode, 502)
assert.match(response.text, /Upstream body delete failure/)
})

it("Falls back to generic RERUM error text for body delete when upstream .text() throws.", async () => {
global.fetch = async () => ({
ok: false,
status: 500,
text: async () => {
throw new Error("text stream consumed")
}
})

const response = await request(routeTester)
.delete("/delete")
.set("Content-Type", "application/json")
.send({ "@id": rerumUri })

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})

it("Preserves upstream text error message for path delete when response is non-ok.", async () => {
global.fetch = async () => ({
ok: false,
status: 503,
text: async () => "Upstream path delete failure"
})

const response = await request(routeTester)
.delete("/delete/00000")

assert.equal(response.statusCode, 502)
assert.match(response.text, /Upstream path delete failure/)
})

it("Falls back to generic RERUM error text for path delete when upstream .text() throws.", async () => {
global.fetch = async () => ({
ok: false,
status: 500,
text: async () => {
throw new Error("text stream consumed")
}
})

const response = await request(routeTester)
.delete("/delete/00000")

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})
})
120 changes: 120 additions & 0 deletions test/routes/error-messenger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ function appWith(routeHandler) {
}

describe("Check shared error messenger behavior. __rest __core", () => {
it("Returns early when headers are already sent.", async () => {
const app = express()
app.get("/test", (req, res, next) => {
res.end("partial")
next(new Error("late error"))
})
app.use(messenger)

const response = await request(app).get("/test")
assert.equal(response.statusCode, 200)
assert.match(response.text, /partial/)
})

it("Returns structured JSON error bodies when upstream responds with JSON.", async () => {
const app = appWith((req, res, next) => {
next({
Expand All @@ -38,6 +51,21 @@ describe("Check shared error messenger behavior. __rest __core", () => {
assert.match(response.text, /boom/)
})

it("Uses statusCode and statusMessage fallback fields when present.", async () => {
const app = appWith((req, res, next) => {
next({
statusCode: 499,
statusMessage: "Client closed request",
headers: { get: () => "text/plain" },
text: async () => ""
})
})

const response = await request(app).get("/test")
assert.equal(response.statusCode, 499)
assert.match(response.text, /Client closed request/)
})

it("Uses fallback message if .text() throws.", async () => {
const app = appWith((req, res, next) => {
next({
Expand All @@ -55,6 +83,20 @@ describe("Check shared error messenger behavior. __rest __core", () => {
assert.match(response.text, /Upstream unavailable/)
})

it("Sends plain text body from upstream text() when provided.", async () => {
const app = appWith((req, res, next) => {
next({
status: 418,
headers: { get: () => "text/plain" },
text: async () => "Teapot exploded"
})
})

const response = await request(app).get("/test")
assert.equal(response.statusCode, 418)
assert.match(response.text, /Teapot exploded/)
})

it("Returns structured payload when error carries payload.", async () => {
const app = appWith((req, res, next) => {
next({
Expand All @@ -68,3 +110,81 @@ describe("Check shared error messenger behavior. __rest __core", () => {
assert.equal(response.body.code, "BAD_INPUT")
})
})

function createMockRes(headersSent = false) {
const res = {
headersSent,
statusCode: null,
sentText: null,
sentJson: null,
setHeaders: {},
status(code) {
this.statusCode = code
return this
},
json(payload) {
this.sentJson = payload
return this
},
send(text) {
this.sentText = text
return this
},
set(name, value) {
this.setHeaders[name] = value
return this
}
}
return res
}

describe("Check shared error messenger unit branches. __core", () => {
it("Returns immediately when headersSent is true.", async () => {
const res = createMockRes(true)
await messenger(new Error("ignored"), {}, res, () => {})
assert.equal(res.statusCode, null)
assert.equal(res.sentText, null)
assert.equal(res.sentJson, null)
})

it("Sends payload JSON when payload is present.", async () => {
const res = createMockRes(false)
await messenger({ status: 409, payload: { code: "CONFLICT" } }, {}, res, () => {})
assert.equal(res.statusCode, 409)
assert.equal(res.sentJson.code, "CONFLICT")
})

it("Uses upstream JSON response when content-type is JSON.", async () => {
const res = createMockRes(false)
await messenger(
{
status: 422,
headers: { get: () => "application/json; charset=utf-8" },
json: async () => ({ detail: "bad request" })
},
{},
res,
() => {}
)
assert.equal(res.statusCode, 422)
assert.equal(res.sentJson.detail, "bad request")
})

it("Sends plain text and sets content-type for non-JSON upstream errors.", async () => {
const res = createMockRes(false)
await messenger(
{
status: 503,
message: "fallback",
headers: { get: () => "text/plain" },
text: async () => "upstream text"
},
{},
res,
() => {}
)
assert.equal(res.statusCode, 503)
assert.equal(res.sentText, "upstream text")
assert.equal(res.setHeaders["Content-Type"], "text/plain; charset=utf-8")
})
})
13 changes: 13 additions & 0 deletions test/routes/index.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import "../helpers/env.js"
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import express from "express"
import request from "supertest"
import app from "../../app.js"
import indexRoute from "../../routes/index.js"

const routeTester = express()
routeTester.use("/", indexRoute)

describe("Make sure TinyNode demo interface is present. __core", () => {
it("/index.html", async () => {
const response = await request(app).get("/index.html")
assert.equal(response.statusCode, 200)
assert.match(response.header["content-type"], /html/)
})

it("Index router returns 405 for unsupported root methods.", async () => {
let response = await request(routeTester).get("/")
assert.equal(response.statusCode, 405)

response = await request(routeTester).post("/")
assert.equal(response.statusCode, 405)
})
})
49 changes: 49 additions & 0 deletions test/routes/overwrite.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,55 @@ describe("Overwrite network failure behavior. __rest __core", () => {
.send({ "@id": rerumTinyTestObjId, testing: "item" })
assert.equal(response.statusCode, 502)
})

it("Preserves upstream text error message for non-409 overwrite failures.", async () => {
global.fetch = async () => ({
ok: false,
status: 503,
text: async () => "Upstream overwrite failure"
})

const response = await request(routeTester)
.put("/overwrite")
.set("Content-Type", "application/json")
.send({ "@id": rerumTinyTestObjId, testing: "item" })

assert.equal(response.statusCode, 502)
assert.match(response.text, /Upstream overwrite failure/)
})

it("Falls back to generic RERUM error text when overwrite upstream .text() throws.", async () => {
global.fetch = async () => ({
ok: false,
status: 500,
text: async () => {
throw new Error("text stream consumed")
}
})

const response = await request(routeTester)
.put("/overwrite")
.set("Content-Type", "application/json")
.send({ "@id": rerumTinyTestObjId, testing: "item" })

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})

it("Maps successful overwrite payload without id fields to 502.", async () => {
global.fetch = async () => ({
ok: true,
json: async () => ({ testing: "item" })
})

const response = await request(routeTester)
.put("/overwrite")
.set("Content-Type", "application/json")
.send({ "@id": rerumTinyTestObjId, testing: "item" })

assert.equal(response.statusCode, 502)
assert.match(response.text, /A RERUM error occurred/)
})
})

describe("Check that the properly used overwrite endpoints function and interact with RERUM. __e2e", () => {
Expand Down
Loading
Loading