@coralai/sps-cli 0.48.1 → 0.49.0

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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * system route tests — env get/patch + info.
3
+ */
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, mkdirSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import { resolve } from 'node:path';
8
+ describe('system route', () => {
9
+ let tmpHome;
10
+ let originalHome;
11
+ beforeEach(() => {
12
+ originalHome = process.env.HOME;
13
+ tmpHome = mkdtempSync(resolve(tmpdir(), 'sps-test-home-'));
14
+ process.env.HOME = tmpHome;
15
+ vi.resetModules();
16
+ });
17
+ afterEach(() => {
18
+ process.env.HOME = originalHome;
19
+ rmSync(tmpHome, { recursive: true, force: true });
20
+ });
21
+ async function buildApp() {
22
+ const { createSystemRoute } = await import('./system.js');
23
+ const { Hono } = await import('hono');
24
+ const app = new Hono();
25
+ app.route('/api/system', createSystemRoute('0.49.0', new Date()));
26
+ return app;
27
+ }
28
+ it('GET /info returns version + runtime', async () => {
29
+ const app = await buildApp();
30
+ const res = await app.request('/api/system/info');
31
+ expect(res.status).toBe(200);
32
+ const body = (await res.json());
33
+ expect(body.version).toBe('0.49.0');
34
+ expect(body.nodeVersion).toMatch(/^v\d+/);
35
+ expect(body.platform).toBe(process.platform);
36
+ });
37
+ it('GET /env reports exists:false when env missing', async () => {
38
+ const app = await buildApp();
39
+ const res = await app.request('/api/system/env');
40
+ expect(res.status).toBe(200);
41
+ const body = (await res.json());
42
+ expect(body.exists).toBe(false);
43
+ expect(body.entries).toEqual([]);
44
+ });
45
+ it('GET /env masks secret keys', async () => {
46
+ mkdirSync(resolve(tmpHome, '.coral'), { recursive: true });
47
+ writeFileSync(resolve(tmpHome, '.coral', 'env'), 'export GITLAB_URL="https://example.com"\nexport GITLAB_TOKEN="glpat-1234567890"\n');
48
+ const app = await buildApp();
49
+ const res = await app.request('/api/system/env');
50
+ expect(res.status).toBe(200);
51
+ const body = (await res.json());
52
+ const url = body.entries.find((e) => e.key === 'GITLAB_URL');
53
+ const token = body.entries.find((e) => e.key === 'GITLAB_TOKEN');
54
+ expect(url?.masked).toBe(false);
55
+ expect(url?.value).toBe('https://example.com');
56
+ expect(token?.masked).toBe(true);
57
+ expect(token?.value).not.toContain('1234567890');
58
+ });
59
+ it('PATCH /env creates file with 0600 on first save', async () => {
60
+ const app = await buildApp();
61
+ const res = await app.request('/api/system/env', {
62
+ method: 'PATCH',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({ content: 'export FOO="bar"\n' }),
65
+ });
66
+ expect(res.status).toBe(200);
67
+ const envPath = resolve(tmpHome, '.coral', 'env');
68
+ expect(existsSync(envPath)).toBe(true);
69
+ const mode = statSync(envPath).mode & 0o777;
70
+ expect(mode).toBe(0o600);
71
+ expect(readFileSync(envPath, 'utf-8')).toContain('FOO="bar"');
72
+ });
73
+ it('PATCH /env rejects without etag when file exists', async () => {
74
+ mkdirSync(resolve(tmpHome, '.coral'), { recursive: true });
75
+ writeFileSync(resolve(tmpHome, '.coral', 'env'), 'existing\n');
76
+ const app = await buildApp();
77
+ const res = await app.request('/api/system/env', {
78
+ method: 'PATCH',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify({ content: 'new' }),
81
+ });
82
+ expect(res.status).toBe(422);
83
+ });
84
+ it('PATCH /env 409 on etag mismatch, 200 on correct etag', async () => {
85
+ mkdirSync(resolve(tmpHome, '.coral'), { recursive: true });
86
+ writeFileSync(resolve(tmpHome, '.coral', 'env'), 'existing\n');
87
+ const app = await buildApp();
88
+ // Read current etag via /env/raw
89
+ const rawRes = await app.request('/api/system/env/raw');
90
+ const { etag } = (await rawRes.json());
91
+ // Wrong etag → 409
92
+ const bad = await app.request('/api/system/env', {
93
+ method: 'PATCH',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify({ content: 'new', etag: 'bogus' }),
96
+ });
97
+ expect(bad.status).toBe(409);
98
+ // Correct etag → 200
99
+ const ok = await app.request('/api/system/env', {
100
+ method: 'PATCH',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({ content: 'updated\n', etag }),
103
+ });
104
+ expect(ok.status).toBe(200);
105
+ });
106
+ });
107
+ //# sourceMappingURL=system.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.test.js","sourceRoot":"","sources":["../../../src/console-server/routes/system.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coralai/sps-cli",
3
- "version": "0.48.1",
3
+ "version": "0.49.0",
4
4
  "description": "SPS CLI — AI-driven development pipeline orchestrator",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -16,6 +16,7 @@
16
16
  "test": "vitest run",
17
17
  "test:watch": "vitest",
18
18
  "test:coverage": "vitest run --coverage",
19
+ "test:e2e": "playwright test",
19
20
  "prepublishOnly": "npm run build"
20
21
  },
21
22
  "engines": {
@@ -37,7 +38,9 @@
37
38
  ],
38
39
  "license": "MIT",
39
40
  "devDependencies": {
41
+ "@axe-core/playwright": "^4.11.2",
40
42
  "@biomejs/biome": "^2.4.10",
43
+ "@playwright/test": "^1.59.1",
41
44
  "@types/node": "^25.5.0",
42
45
  "@vitest/coverage-v8": "^4.1.2",
43
46
  "knip": "^6.3.0",