@openparachute/hub 0.5.10-rc.4 → 0.5.10-rc.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openparachute/hub",
3
- "version": "0.5.10-rc.4",
3
+ "version": "0.5.10-rc.5",
4
4
  "description": "parachute — the local hub for the Parachute ecosystem (discovery, ports, lifecycle, soon OAuth).",
5
5
  "license": "AGPL-3.0",
6
6
  "publishConfig": {
@@ -979,10 +979,12 @@ describe("hubFetch routing", () => {
979
979
  }
980
980
  });
981
981
 
982
- // First-boot setup placeholder (hub#258). When no admin exists the page
983
- // is the bootstrap onboarding surface; once an admin exists it 301s to
984
- // /login so a stale bookmark still lands somewhere useful.
985
- test("/admin/setup renders placeholder HTML when no admin exists", async () => {
982
+ // First-boot setup wizard (hub#259, expanding hub#258's static
983
+ // placeholder). When no admin exists, GET /admin/setup renders the
984
+ // wizard's account-step form. Once admin + vault both exist, it 301s
985
+ // to /login so a stale bookmark still lands somewhere useful. With
986
+ // admin but no vault, the wizard resumes at the vault step.
987
+ test("/admin/setup renders the wizard's account form when no admin exists", async () => {
986
988
  const h = makeHarness();
987
989
  try {
988
990
  const db = openHubDb(hubDbPath(h.dir));
@@ -991,7 +993,8 @@ describe("hubFetch routing", () => {
991
993
  expect(res.status).toBe(200);
992
994
  expect(res.headers.get("content-type")).toContain("text/html");
993
995
  const body = await res.text();
994
- expect(body).toContain("first-boot setup");
996
+ expect(body).toContain('action="/admin/setup/account"');
997
+ // Env-var seed path is still surfaced as the alt-path disclosure.
995
998
  expect(body).toContain("PARACHUTE_INITIAL_ADMIN_USERNAME");
996
999
  } finally {
997
1000
  db.close();
@@ -1001,13 +1004,35 @@ describe("hubFetch routing", () => {
1001
1004
  }
1002
1005
  });
1003
1006
 
1004
- test("/admin/setup 301s to /login when an admin already exists", async () => {
1007
+ test("/admin/setup 301s to /login once admin + vault both exist (hub#259)", async () => {
1005
1008
  const h = makeHarness();
1006
1009
  try {
1007
1010
  const db = openHubDb(hubDbPath(h.dir));
1008
1011
  try {
1009
1012
  await createUser(db, "owner", "pw");
1010
- const res = await hubFetch(h.dir, { getDb: () => db })(req("/admin/setup"));
1013
+ // Seed the vault entry so the wizard's state derives as "done"
1014
+ // and the GET 301s. Without this the wizard would still resume
1015
+ // at the vault step.
1016
+ const { writeManifest } = await import("../services-manifest.ts");
1017
+ const { join } = await import("node:path");
1018
+ writeManifest(
1019
+ {
1020
+ services: [
1021
+ {
1022
+ name: "parachute-vault",
1023
+ version: "0.1.0",
1024
+ port: 1940,
1025
+ paths: ["/vault/default"],
1026
+ health: "/health",
1027
+ },
1028
+ ],
1029
+ },
1030
+ join(h.dir, "services.json"),
1031
+ );
1032
+ const res = await hubFetch(h.dir, {
1033
+ getDb: () => db,
1034
+ manifestPath: join(h.dir, "services.json"),
1035
+ })(req("/admin/setup"));
1011
1036
  expect(res.status).toBe(301);
1012
1037
  expect(res.headers.get("location")).toBe("/login");
1013
1038
  } finally {
@@ -141,16 +141,17 @@ describe("setup gate (no admin yet)", () => {
141
141
  }
142
142
  });
143
143
 
144
- test("/admin/setup renders the placeholder HTML", async () => {
144
+ test("/admin/setup renders the wizard (account step) when no admin exists", async () => {
145
145
  const db = openHubDb(hubDbPath(h.dir));
146
146
  try {
147
147
  const res = await hubFetch(h.dir, { getDb: () => db })(req("/admin/setup"));
148
148
  expect(res.status).toBe(200);
149
149
  expect(res.headers.get("content-type")).toContain("text/html");
150
150
  const html = await res.text();
151
- // Spot-check the env-var seed is documented it's the canonical
152
- // bootstrap path for containers and we don't want a future
153
- // refactor to silently strip it.
151
+ // Spot-check the wizard is rendering its account-step form (hub#259
152
+ // replaced the env-var-only placeholder with a real wizard, but the
153
+ // env-var path is still surfaced as the "alt-path" disclosure).
154
+ expect(html).toContain('action="/admin/setup/account"');
154
155
  expect(html).toContain("PARACHUTE_INITIAL_ADMIN_USERNAME");
155
156
  expect(html).toContain("PARACHUTE_INITIAL_ADMIN_PASSWORD");
156
157
  } finally {
@@ -182,13 +183,22 @@ describe("setup gate (admin exists)", () => {
182
183
  }
183
184
  });
184
185
 
185
- test("/admin/setup 301s to /login once an admin exists", async () => {
186
+ test("/admin/setup resumes at the vault step when admin exists but vault doesn't (hub#259)", async () => {
186
187
  const db = openHubDb(hubDbPath(h.dir));
187
188
  try {
188
189
  await createUser(db, "owner", "pw");
189
- const res = await hubFetch(h.dir, { getDb: () => db })(req("/admin/setup"));
190
- expect(res.status).toBe(301);
191
- expect(res.headers.get("location")).toBe("/login");
190
+ const res = await hubFetch(h.dir, {
191
+ getDb: () => db,
192
+ manifestPath: join(h.dir, "services.json"),
193
+ })(req("/admin/setup"));
194
+ // With admin in place but no vault entry in services.json, the
195
+ // wizard's GET resumes at step 3 — the vault-name form — rather
196
+ // than 301-ing to /login. The 301-to-/login fires only once BOTH
197
+ // admin and vault are in place; that case is exercised in the
198
+ // setup-wizard suite where the manifest is seeded.
199
+ expect(res.status).toBe(200);
200
+ const html = await res.text();
201
+ expect(html).toContain('action="/admin/setup/vault"');
192
202
  } finally {
193
203
  db.close();
194
204
  }