diff --git a/tests/scripting.test.ts b/tests/scripting.test.ts index 7bd439a..3ac2416 100644 --- a/tests/scripting.test.ts +++ b/tests/scripting.test.ts @@ -1,13 +1,18 @@ import { beforeAll, describe, expect, test } from "bun:test"; -import { createTestWorkspace, type TestWorkspace } from "./helpers.ts"; -import { jsonMock } from "./mock-http.ts"; +import { VERSION } from "../cli/src/version.ts"; +import { createTestWorkspace, type TestWorkspace } from "@/../tests/helpers.ts"; +import { jsonMock } from "@/../tests/mock-http.ts"; + +const UPDATE_TEST_VERSION = `${Number(VERSION.split(".")[0]) + 1}.0.0`; const statusMocks = { auth: [jsonMock("GET", "/whoami", { error: "invalid key" }, 401)], forbidden: [jsonMock("GET", "/whoami", { error: "forbidden" }, 403)], rate: [jsonMock("GET", "/whoami", { error: "rate limited" }, 429)], server: [jsonMock("GET", "/whoami", { error: "internal server error" }, 500)], - missing: [jsonMock("GET", "/environments/production/connections/missing", { error: "not found" }, 404)], + missing: [ + jsonMock("GET", "/environments/production/connections/missing", { error: "not found" }, 404), + ], conflict: [jsonMock("POST", "/service_accounts", { error: "conflict" }, 409)], validation: [jsonMock("POST", "/service_accounts", { error: "validation failed" }, 422)], }; @@ -58,11 +63,29 @@ describe("scriptable exit codes and JSON errors", () => { test.each([ ["auth", statusMocks.auth, "altertable --json api GET /whoami", 2, "auth_failed"], - ["not found", statusMocks.missing, "altertable --json api GET /environments/production/connections/missing", 4, undefined], + [ + "not found", + statusMocks.missing, + "altertable --json api GET /environments/production/connections/missing", + 4, + undefined, + ], ["forbidden", statusMocks.forbidden, "altertable --json api GET /whoami", 3, "forbidden"], - ["conflict", statusMocks.conflict, "altertable --json api POST /service_accounts -f label=dup", 5, "conflict"], + [ + "conflict", + statusMocks.conflict, + "altertable --json api POST /service_accounts -f label=dup", + 5, + "conflict", + ], ["rate limit", statusMocks.rate, "altertable --json api GET /whoami", 7, "rate_limited"], - ["validation", statusMocks.validation, "altertable --json api POST /service_accounts -f label=bad", 6, "validation_error"], + [ + "validation", + statusMocks.validation, + "altertable --json api POST /service_accounts -f label=bad", + 6, + "validation_error", + ], ["server error", statusMocks.server, "altertable --json api GET /whoami", 8, "server_error"], ])("%s failure emits JSON error envelope", async (_name, mock, command, exitCode, code) => { await workspace.setupMockHttp(mock); @@ -78,11 +101,18 @@ describe("scriptable exit codes and JSON errors", () => { }); test("missing management credentials exits 10 with configuration_error", async () => { - const isolated = await createTestWorkspace({ ALTERTABLE_API_KEY: undefined, ALTERTABLE_ENV: undefined }); + const isolated = await createTestWorkspace({ + ALTERTABLE_API_KEY: undefined, + ALTERTABLE_ENV: undefined, + }); try { const result = await isolated.runCommand("altertable --json api GET /whoami"); expect(result.exitCode).toBe(10); - expect(JSON.parse(result.stderr)).toMatchObject({ error: true, exit_code: 10, code: "configuration_error" }); + expect(JSON.parse(result.stderr)).toMatchObject({ + error: true, + exit_code: 10, + code: "configuration_error", + }); } finally { await isolated.cleanup(); } @@ -90,18 +120,31 @@ describe("scriptable exit codes and JSON errors", () => { test("network errors exit 9 with network_error", async () => { const result = await workspace.runCommand("altertable --json api GET /whoami", { - env: { ALTERTABLE_MANAGEMENT_API_BASE: "http://127.0.0.1:1", ALTERTABLE_MOCK_HTTP_FILE: undefined }, + env: { + ALTERTABLE_MANAGEMENT_API_BASE: "http://127.0.0.1:1", + ALTERTABLE_MOCK_HTTP_FILE: undefined, + }, }); expect(result.exitCode).toBe(9); - expect(JSON.parse(result.stderr)).toMatchObject({ error: true, exit_code: 9, code: "network_error" }); + expect(JSON.parse(result.stderr)).toMatchObject({ + error: true, + exit_code: 9, + code: "network_error", + }); }); test("profile show missing uses configuration error semantics", async () => { - const result = await workspace.runCommand("altertable --json profile show --name missing-profile"); + const result = await workspace.runCommand( + "altertable --json profile show --name missing-profile", + ); expect(result.exitCode).toBe(10); - expect(JSON.parse(result.stderr)).toMatchObject({ error: true, exit_code: 10, code: "configuration_error" }); + expect(JSON.parse(result.stderr)).toMatchObject({ + error: true, + exit_code: 10, + code: "configuration_error", + }); }); test("usage errors exit 1", async () => { @@ -111,18 +154,26 @@ describe("scriptable exit codes and JSON errors", () => { }); test.each(["update", "upgrade"])("%s checks an explicit version", async (command) => { - const result = await workspace.runCommand(`altertable ${command} 1.2.0 --check`); + const result = await workspace.runCommand( + `altertable ${command} ${UPDATE_TEST_VERSION} --check`, + ); expect(result.exitCode).toBe(0); - expect(result.stdout).toContain("Target version v1.2.0 is already installed."); + expect(result.stdout).toContain( + `A new version of altertable is available: v${UPDATE_TEST_VERSION}`, + ); }); test("update accepts inherited global flags after its arguments", async () => { - const json = await workspace.runCommand("altertable update 1.2.0 --check --json"); - const plain = await workspace.runCommand("altertable update 1.2.0 --check --no-color"); + const json = await workspace.runCommand( + `altertable update ${UPDATE_TEST_VERSION} --check --json`, + ); + const plain = await workspace.runCommand( + `altertable update ${UPDATE_TEST_VERSION} --check --no-color`, + ); expect(json.exitCode).toBe(0); - expect(JSON.parse(json.stdout).latest_version).toBe("1.2.0"); + expect(JSON.parse(json.stdout).latest_version).toBe(UPDATE_TEST_VERSION); expect(plain.exitCode).toBe(0); expect(plain.stdout).not.toContain("\u001B["); });