rollbridge 0.1.54 → 0.1.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -61,6 +61,11 @@ external-supervisor migration path, not that atomic handoff.
61
61
 
62
62
  ## Validation and publication
63
63
 
64
+ All tests use `@velocious/testing` declarations, hooks, and `expect` matchers.
65
+ Do not import `node:test` or `node:assert`, or add assertion compatibility wrappers.
66
+ Run every changed test file locally with `npx --no-install velocious-test test/<name>.test.js`
67
+ and require it to pass before committing or pushing; lint or CI is not a substitute.
68
+
64
69
  The project is ESM JavaScript with JSDoc type checking. Package scripts are the
65
70
  source of truth: `npm run typecheck`, `npm run lint`, `npm test`, and the combined
66
71
  `npm run all-checks`. Run focused checks for the files changed; documentation-only
@@ -0,0 +1 @@
1
+ - Migrated all Rollbridge tests to `@velocious/testing` declarations, lifecycle APIs, and native `expect` matchers. Lint now rejects Node test and assertion imports in tests.
package/eslint.config.js CHANGED
@@ -81,6 +81,14 @@ export default defineConfig([
81
81
  "no-unused-vars": ["error", {argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_"}]
82
82
  }
83
83
  },
84
+ {
85
+ files: ["test/**/*.js"],
86
+ rules: {
87
+ "no-restricted-imports": ["error", {
88
+ patterns: [{group: ["node:test", "node:test/*", "test", "node:assert", "node:assert/*", "assert", "assert/*"], message: "Use @velocious/testing declarations and expect matchers."}]
89
+ }]
90
+ }
91
+ },
84
92
  jsdoc({
85
93
  config: "flat/recommended",
86
94
  files: ["**/*.{js,mjs,cjs}", "**/bin/rollbridge"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rollbridge",
3
- "version": "0.1.54",
3
+ "version": "0.1.55",
4
4
  "description": "Zero-downtime process supervisor and local traffic switcher for deploy-managed apps.",
5
5
  "keywords": [
6
6
  "deploy",
@@ -29,7 +29,7 @@
29
29
  "all-checks": "npm run typecheck && npm run lint && npm test",
30
30
  "lint": "npm run eslint && npm run typecheck",
31
31
  "release:patch": "release-patch",
32
- "test": "node --test test/*.test.js",
32
+ "test": "velocious-test test/*.test.js",
33
33
  "typecheck": "tsc --noEmit",
34
34
  "eslint": "eslint"
35
35
  },
@@ -44,6 +44,7 @@
44
44
  "@eslint/js": "^10.0.1",
45
45
  "@types/http-proxy": "^1.17.17",
46
46
  "@types/node": "^25.9.1",
47
+ "@velocious/testing": "^0.0.12",
47
48
  "eslint": "^10.4.0",
48
49
  "eslint-plugin-jsdoc": "^62.9.0",
49
50
  "globals": "^17.6.0",
@@ -1,9 +1,10 @@
1
1
  // @ts-check
2
2
 
3
- import assert from "node:assert/strict"
4
- import test from "node:test"
3
+ import {describe, expect, test} from "@velocious/testing"
5
4
  import {runCli} from "../src/cli.js"
6
5
 
6
+ describe("completion", () => {
7
+
7
8
  /**
8
9
  * Runs the CLI while capturing stdout, stderr, and the resulting exit code.
9
10
  * @param {string[]} argv - Process argv.
@@ -39,29 +40,30 @@ async function capture(argv) {
39
40
  test("completion bash prints a sourceable script with commands and option flags", async () => {
40
41
  const {code, output} = await capture(["node", "rollbridge", "completion", "bash"])
41
42
 
42
- assert.notEqual(code, 1)
43
- assert.match(output, /complete -F _rollbridge rollbridge/)
44
- assert.match(output, /compgen -W "daemon deploy rollback recover-generation-transition ensure-daemon status stop restart shutdown validate doctor logs events predeploy-cleanup recover completion"/)
43
+ expect(code).not.toBe(1)
44
+ expect(output).toMatch(/complete -F _rollbridge rollbridge/)
45
+ expect(output).toMatch(/compgen -W "daemon deploy rollback recover-generation-transition ensure-daemon status stop restart shutdown validate doctor logs events predeploy-cleanup recover completion"/)
45
46
  // A command's own options are completed after the command.
46
- assert.match(output, /deploy\)\n\s+opts="[^"]*--release-path[^"]*"/)
47
- assert.match(output, /recover-generation-transition\)\n\s+opts="--config --release-path --release-id --revision --previous-release-id --accept-retired-incumbent"/)
48
- assert.match(output, /ensure-daemon\)\n\s+opts="[^"]*--daemon-runtime-path[^"]*"/)
49
- assert.match(output, /restart\)\n\s+opts="[^"]*--policy[^"]*"/)
47
+ expect(output).toMatch(/deploy\)\n\s+opts="[^"]*--release-path[^"]*"/)
48
+ expect(output).toMatch(/recover-generation-transition\)\n\s+opts="--config --release-path --release-id --revision --previous-release-id --accept-retired-incumbent"/)
49
+ expect(output).toMatch(/ensure-daemon\)\n\s+opts="[^"]*--daemon-runtime-path[^"]*"/)
50
+ expect(output).toMatch(/restart\)\n\s+opts="[^"]*--policy[^"]*"/)
50
51
  })
51
52
 
52
53
  test("completion zsh prints a #compdef script with per-command options", async () => {
53
54
  const {output} = await capture(["node", "rollbridge", "completion", "zsh"])
54
55
 
55
- assert.match(output, /^#compdef rollbridge/)
56
- assert.match(output, /compdef _rollbridge rollbridge/)
57
- assert.match(output, /commands=\(daemon deploy rollback recover-generation-transition ensure-daemon status stop restart shutdown validate doctor logs events predeploy-cleanup recover completion\)/)
58
- assert.match(output, /recover-generation-transition\) compadd -- --config --release-path --release-id --revision --previous-release-id --accept-retired-incumbent/)
59
- assert.match(output, /events\) compadd -- [^\n]*--limit/)
56
+ expect(output).toMatch(/^#compdef rollbridge/)
57
+ expect(output).toMatch(/compdef _rollbridge rollbridge/)
58
+ expect(output).toMatch(/commands=\(daemon deploy rollback recover-generation-transition ensure-daemon status stop restart shutdown validate doctor logs events predeploy-cleanup recover completion\)/)
59
+ expect(output).toMatch(/recover-generation-transition\) compadd -- --config --release-path --release-id --revision --previous-release-id --accept-retired-incumbent/)
60
+ expect(output).toMatch(/events\) compadd -- [^\n]*--limit/)
60
61
  })
61
62
 
62
63
  test("completion rejects an unsupported shell with a non-zero exit code", async () => {
63
64
  const {code, errorOutput} = await capture(["node", "rollbridge", "completion", "fish"])
64
65
 
65
- assert.equal(code, 1)
66
- assert.match(errorOutput, /Unsupported shell "fish"\. Supported shells: bash, zsh\./)
66
+ expect(code).toBe(1)
67
+ expect(errorOutput).toMatch(/Unsupported shell "fish"\. Supported shells: bash, zsh\./)
68
+ })
67
69
  })
@@ -1,34 +1,32 @@
1
1
  // @ts-check
2
2
 
3
- import assert from "node:assert/strict"
4
3
  import fs from "node:fs/promises"
5
4
  import os from "node:os"
6
5
  import path from "node:path"
7
- import test from "node:test"
6
+ import {describe, expect, test} from "@velocious/testing"
8
7
  import {fileURLToPath} from "node:url"
9
8
  import {loadConfig} from "../src/config.js"
10
9
 
10
+ describe("config-examples", () => {
11
+
11
12
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
12
13
 
13
14
  test("TensorBuzz example config loads", async () => {
14
15
  const config = await loadConfig(path.join(repoRoot, "examples", "tensorbuzz.com.js"))
15
16
 
16
- assert.equal(config.application, "tensorbuzz")
17
- assert.equal(config.control.path, "/tmp/rollbridge-tensorbuzz.sock")
18
- assert.equal(config.proxy.host, "127.0.0.1")
19
- assert.equal(config.proxy.port, 4500)
20
- assert.equal(config.proxy.healthPath, "/ping")
21
- assert.deepEqual(
22
- config.processes.map((processConfig) => [processConfig.id, processConfig.policy]),
23
- [
17
+ expect(config.application).toBe("tensorbuzz")
18
+ expect(config.control.path).toBe("/tmp/rollbridge-tensorbuzz.sock")
19
+ expect(config.proxy.host).toBe("127.0.0.1")
20
+ expect(config.proxy.port).toBe(4500)
21
+ expect(config.proxy.healthPath).toBe("/ping")
22
+ expect(config.processes.map((processConfig) => [processConfig.id, processConfig.policy])).toEqual([
24
23
  ["beacon", "service"],
25
24
  ["background-jobs-main", "service"],
26
25
  ["background-jobs-worker", "companion"],
27
26
  ["web", "proxied"]
28
- ]
29
- )
30
- assert.equal(config.processes[2].lifecycle.reactivateCommand, "appctl jobs-worker-reactivate --pid $ROLLBRIDGE_PID")
31
- assert.equal(config.processes[3].env.VELOCIOUS_BACKGROUND_JOBS_PORT, "{{ports.background-jobs-main}}")
27
+ ])
28
+ expect(config.processes[2].lifecycle.reactivateCommand).toBe("appctl jobs-worker-reactivate --pid $ROLLBRIDGE_PID")
29
+ expect(config.processes[3].env.VELOCIOUS_BACKGROUND_JOBS_PORT).toBe("{{ports.background-jobs-main}}")
32
30
  })
33
31
 
34
32
  test("loadConfig resolves a config module that exports a function", async () => {
@@ -50,11 +48,12 @@ test("loadConfig resolves a config module that exports a function", async () =>
50
48
  try {
51
49
  const config = await loadConfig(configPath)
52
50
 
53
- assert.equal(config.application, "computed-app")
54
- assert.equal(config.proxy.port, 8190)
55
- assert.equal(config.processes[0].id, "web")
51
+ expect(config.application).toBe("computed-app")
52
+ expect(config.proxy.port).toBe(8190)
53
+ expect(config.processes[0].id).toBe("web")
56
54
  } finally {
57
55
  delete process.env.ROLLBRIDGE_TEST_APP
58
56
  await fs.rm(dir, {force: true, recursive: true})
59
57
  }
60
58
  })
59
+ })
@@ -1,13 +1,14 @@
1
1
  // @ts-check
2
2
 
3
- import assert from "node:assert/strict"
4
3
  import fs from "node:fs/promises"
5
4
  import os from "node:os"
6
5
  import path from "node:path"
7
- import test from "node:test"
6
+ import {describe, expect, test} from "@velocious/testing"
8
7
  import {loadConfig, resolveConfigPath} from "../src/config.js"
9
8
  import {runCli} from "../src/cli.js"
10
9
 
10
+ describe("config-path", () => {
11
+
11
12
  const validConfig = {
12
13
  application: "demo",
13
14
  control: {path: "/tmp/rollbridge-config-path.sock"},
@@ -34,7 +35,7 @@ test("resolveConfigPath returns an explicit path unchanged", async () => {
34
35
  const dir = await fs.mkdtemp(path.join(os.tmpdir(), "rollbridge-cfgpath-"))
35
36
 
36
37
  try {
37
- assert.equal(await resolveConfigPath("/somewhere/custom.js", dir), "/somewhere/custom.js")
38
+ expect(await resolveConfigPath("/somewhere/custom.js", dir)).toBe("/somewhere/custom.js")
38
39
  } finally {
39
40
  await fs.rm(dir, {force: true, recursive: true})
40
41
  }
@@ -46,7 +47,7 @@ test("resolveConfigPath resolves rollbridge.js in the working directory", async
46
47
  try {
47
48
  await writeConfigModule(dir)
48
49
 
49
- assert.equal(await resolveConfigPath(undefined, dir), path.join(dir, "rollbridge.js"))
50
+ expect(await resolveConfigPath(undefined, dir)).toBe(path.join(dir, "rollbridge.js"))
50
51
  } finally {
51
52
  await fs.rm(dir, {force: true, recursive: true})
52
53
  }
@@ -56,10 +57,7 @@ test("resolveConfigPath throws an actionable error when no default config exists
56
57
  const dir = await fs.mkdtemp(path.join(os.tmpdir(), "rollbridge-cfgpath-"))
57
58
 
58
59
  try {
59
- await assert.rejects(
60
- () => resolveConfigPath(undefined, dir),
61
- /No config file found.*rollbridge\.js/
62
- )
60
+ await expect(resolveConfigPath(undefined, dir)).rejects.toThrow(/No config file found.*rollbridge\.js/)
63
61
  } finally {
64
62
  await fs.rm(dir, {force: true, recursive: true})
65
63
  }
@@ -70,11 +68,11 @@ test("loadConfig reloads a changed CommonJS module", async () => {
70
68
  const configPath = await writeConfigModule(dir)
71
69
 
72
70
  try {
73
- assert.equal((await loadConfig(configPath)).application, "demo")
71
+ expect((await loadConfig(configPath)).application).toBe("demo")
74
72
 
75
73
  await fs.writeFile(configPath, `module.exports = ${JSON.stringify({...validConfig, application: "updated"}, null, 2)}\n`)
76
74
 
77
- assert.equal((await loadConfig(configPath)).application, "updated")
75
+ expect((await loadConfig(configPath)).application).toBe("updated")
78
76
  } finally {
79
77
  await fs.rm(dir, {force: true, recursive: true})
80
78
  }
@@ -101,5 +99,6 @@ test("validate CLI command resolves the default config when --config is omitted"
101
99
  await fs.rm(dir, {force: true, recursive: true})
102
100
  }
103
101
 
104
- assert.match(lines.join("\n"), /rollbridge\.js is valid: 1 process, proxy on 127\.0\.0\.1:8182\./)
102
+ expect(lines.join("\n")).toMatch(/rollbridge\.js is valid: 1 process, proxy on 127\.0\.0\.1:8182\./)
103
+ })
105
104
  })