@project-ajax/sdk 0.0.59 → 0.0.61
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/dist/builder.d.ts +6 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +4 -0
- package/dist/capabilities/sync.d.ts +10 -3
- package/dist/capabilities/sync.d.ts.map +1 -1
- package/dist/schema.d.ts +7 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +4 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -11
- package/src/builder.ts +15 -1
- package/src/capabilities/sync.ts +18 -3
- package/src/schema.ts +11 -2
- package/src/types.ts +15 -0
- package/dist/cli/api/client.d.ts +0 -212
- package/dist/cli/api/client.d.ts.map +0 -1
- package/dist/cli/api/client.js +0 -330
- package/dist/cli/api/result.d.ts +0 -43
- package/dist/cli/api/result.d.ts.map +0 -1
- package/dist/cli/api/result.js +0 -43
- package/dist/cli/bin/cli.d.ts +0 -3
- package/dist/cli/bin/cli.d.ts.map +0 -1
- package/dist/cli/bin/cli.js +0 -5
- package/dist/cli/commands/auth.d.ts +0 -2
- package/dist/cli/commands/auth.d.ts.map +0 -1
- package/dist/cli/commands/auth.impl.d.ts +0 -5
- package/dist/cli/commands/auth.impl.d.ts.map +0 -1
- package/dist/cli/commands/auth.impl.js +0 -45
- package/dist/cli/commands/auth.impl.test.d.ts +0 -2
- package/dist/cli/commands/auth.impl.test.d.ts.map +0 -1
- package/dist/cli/commands/auth.js +0 -56
- package/dist/cli/commands/bundle.d.ts +0 -2
- package/dist/cli/commands/bundle.d.ts.map +0 -1
- package/dist/cli/commands/bundle.impl.d.ts +0 -2
- package/dist/cli/commands/bundle.impl.d.ts.map +0 -1
- package/dist/cli/commands/bundle.impl.js +0 -21
- package/dist/cli/commands/bundle.impl.test.d.ts +0 -2
- package/dist/cli/commands/bundle.impl.test.d.ts.map +0 -1
- package/dist/cli/commands/bundle.js +0 -23
- package/dist/cli/commands/capabilities.d.ts +0 -2
- package/dist/cli/commands/capabilities.d.ts.map +0 -1
- package/dist/cli/commands/capabilities.impl.d.ts +0 -3
- package/dist/cli/commands/capabilities.impl.d.ts.map +0 -1
- package/dist/cli/commands/capabilities.impl.js +0 -40
- package/dist/cli/commands/capabilities.js +0 -24
- package/dist/cli/commands/connect.d.ts +0 -2
- package/dist/cli/commands/connect.d.ts.map +0 -1
- package/dist/cli/commands/connect.impl.d.ts +0 -6
- package/dist/cli/commands/connect.impl.d.ts.map +0 -1
- package/dist/cli/commands/connect.impl.js +0 -116
- package/dist/cli/commands/connect.js +0 -78
- package/dist/cli/commands/deploy.d.ts +0 -3
- package/dist/cli/commands/deploy.d.ts.map +0 -1
- package/dist/cli/commands/deploy.impl.d.ts +0 -6
- package/dist/cli/commands/deploy.impl.d.ts.map +0 -1
- package/dist/cli/commands/deploy.impl.js +0 -60
- package/dist/cli/commands/deploy.impl.test.d.ts +0 -2
- package/dist/cli/commands/deploy.impl.test.d.ts.map +0 -1
- package/dist/cli/commands/deploy.js +0 -22
- package/dist/cli/commands/env.d.ts +0 -2
- package/dist/cli/commands/env.d.ts.map +0 -1
- package/dist/cli/commands/env.impl.d.ts +0 -11
- package/dist/cli/commands/env.impl.d.ts.map +0 -1
- package/dist/cli/commands/env.impl.js +0 -62
- package/dist/cli/commands/env.js +0 -39
- package/dist/cli/commands/exec.d.ts +0 -3
- package/dist/cli/commands/exec.d.ts.map +0 -1
- package/dist/cli/commands/exec.impl.d.ts +0 -7
- package/dist/cli/commands/exec.impl.d.ts.map +0 -1
- package/dist/cli/commands/exec.impl.js +0 -123
- package/dist/cli/commands/exec.js +0 -30
- package/dist/cli/commands/runs.d.ts +0 -2
- package/dist/cli/commands/runs.d.ts.map +0 -1
- package/dist/cli/commands/runs.impl.d.ts +0 -4
- package/dist/cli/commands/runs.impl.d.ts.map +0 -1
- package/dist/cli/commands/runs.impl.js +0 -71
- package/dist/cli/commands/runs.js +0 -45
- package/dist/cli/commands/secrets.d.ts +0 -2
- package/dist/cli/commands/secrets.d.ts.map +0 -1
- package/dist/cli/commands/secrets.impl.d.ts +0 -5
- package/dist/cli/commands/secrets.impl.d.ts.map +0 -1
- package/dist/cli/commands/secrets.impl.js +0 -99
- package/dist/cli/commands/secrets.js +0 -64
- package/dist/cli/commands/utils/testing.d.ts +0 -13
- package/dist/cli/commands/utils/testing.d.ts.map +0 -1
- package/dist/cli/commands/utils/testing.js +0 -58
- package/dist/cli/config.d.ts +0 -63
- package/dist/cli/config.d.ts.map +0 -1
- package/dist/cli/config.js +0 -194
- package/dist/cli/config.test.d.ts +0 -2
- package/dist/cli/config.test.d.ts.map +0 -1
- package/dist/cli/context.d.ts +0 -15
- package/dist/cli/context.d.ts.map +0 -1
- package/dist/cli/context.js +0 -16
- package/dist/cli/deploy.d.ts +0 -37
- package/dist/cli/deploy.d.ts.map +0 -1
- package/dist/cli/deploy.js +0 -100
- package/dist/cli/flags.d.ts +0 -21
- package/dist/cli/flags.d.ts.map +0 -1
- package/dist/cli/flags.js +0 -49
- package/dist/cli/handler.d.ts +0 -14
- package/dist/cli/handler.d.ts.map +0 -1
- package/dist/cli/handler.js +0 -32
- package/dist/cli/io.d.ts +0 -55
- package/dist/cli/io.d.ts.map +0 -1
- package/dist/cli/io.js +0 -96
- package/dist/cli/routes.d.ts +0 -2
- package/dist/cli/routes.d.ts.map +0 -1
- package/dist/cli/routes.js +0 -62
- package/dist/cli/utils/array.d.ts +0 -2
- package/dist/cli/utils/array.d.ts.map +0 -1
- package/dist/cli/utils/array.js +0 -10
- package/dist/cli/utils/openUrl.d.ts +0 -4
- package/dist/cli/utils/openUrl.d.ts.map +0 -1
- package/dist/cli/utils/openUrl.js +0 -43
- package/dist/cli/utils/string.d.ts +0 -2
- package/dist/cli/utils/string.d.ts.map +0 -1
- package/dist/cli/utils/string.js +0 -12
- package/src/cli/api/client.ts +0 -628
- package/src/cli/api/result.ts +0 -71
- package/src/cli/bin/cli.ts +0 -7
- package/src/cli/commands/.cursor/rules/testing-commands.mdc +0 -212
- package/src/cli/commands/auth.impl.test.ts +0 -228
- package/src/cli/commands/auth.impl.ts +0 -56
- package/src/cli/commands/auth.ts +0 -63
- package/src/cli/commands/bundle.impl.test.ts +0 -143
- package/src/cli/commands/bundle.impl.ts +0 -21
- package/src/cli/commands/bundle.ts +0 -23
- package/src/cli/commands/capabilities.impl.ts +0 -47
- package/src/cli/commands/capabilities.ts +0 -25
- package/src/cli/commands/connect.impl.ts +0 -149
- package/src/cli/commands/connect.ts +0 -80
- package/src/cli/commands/deploy.impl.test.ts +0 -254
- package/src/cli/commands/deploy.impl.ts +0 -73
- package/src/cli/commands/deploy.ts +0 -22
- package/src/cli/commands/env.impl.ts +0 -88
- package/src/cli/commands/env.ts +0 -38
- package/src/cli/commands/exec.impl.ts +0 -171
- package/src/cli/commands/exec.ts +0 -32
- package/src/cli/commands/runs.impl.ts +0 -87
- package/src/cli/commands/runs.ts +0 -49
- package/src/cli/commands/secrets.impl.ts +0 -130
- package/src/cli/commands/secrets.ts +0 -73
- package/src/cli/commands/utils/testing.ts +0 -66
- package/src/cli/config.test.ts +0 -108
- package/src/cli/config.ts +0 -265
- package/src/cli/context.ts +0 -26
- package/src/cli/deploy.ts +0 -190
- package/src/cli/flags.ts +0 -72
- package/src/cli/handler.ts +0 -68
- package/src/cli/io.ts +0 -132
- package/src/cli/routes.ts +0 -61
- package/src/cli/utils/array.ts +0 -7
- package/src/cli/utils/openUrl.ts +0 -53
- package/src/cli/utils/string.ts +0 -9
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Testing patterns for CLI command implementations
|
|
3
|
-
globs:
|
|
4
|
-
- "**/*.impl.test.ts"
|
|
5
|
-
alwaysApply: false
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Testing CLI Commands
|
|
9
|
-
|
|
10
|
-
## Understanding buildHandler
|
|
11
|
-
|
|
12
|
-
Commands are wrapped with `buildHandler` which:
|
|
13
|
-
1. Returns a function that accepts `(flags, ...args)`
|
|
14
|
-
2. Internally loads config using `Config.load()`
|
|
15
|
-
3. Requires a `this` context with `process`, `fs`, `os`, `path`, and `writer`
|
|
16
|
-
|
|
17
|
-
## Test Structure Pattern
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
describe("commandName", () => {
|
|
21
|
-
let stderrSpy: Mock<typeof process.stderr.write>; // or stdoutSpy
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
stderrSpy = vi
|
|
25
|
-
.spyOn(process.stderr, "write")
|
|
26
|
-
.mockImplementation(() => true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("does something", async () => {
|
|
30
|
-
// 1. Create a mock config with the desired state
|
|
31
|
-
const mockConfig = await createTestConfig({
|
|
32
|
-
token: "test-token",
|
|
33
|
-
workerId: null,
|
|
34
|
-
environment: "local",
|
|
35
|
-
baseUrl: "http://localhost:3000",
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// 2. Spy on Config.load to return your mock config
|
|
39
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
40
|
-
|
|
41
|
-
// 3. Spy on config methods to verify they're called
|
|
42
|
-
const setTokenSpy = vi.spyOn(mockConfig, "setToken");
|
|
43
|
-
|
|
44
|
-
// 4. Create a base context
|
|
45
|
-
const context = createBaseContext();
|
|
46
|
-
|
|
47
|
-
// 5. Call the handler with .call(context, flags, ...args)
|
|
48
|
-
await commandHandler.call(context, baseFlags, "arg1", "arg2");
|
|
49
|
-
|
|
50
|
-
// 6. Assert on spies and output
|
|
51
|
-
expect(setTokenSpy).toHaveBeenCalledWith("expected-value");
|
|
52
|
-
expect(stderrSpy).toHaveBeenCalledWith(
|
|
53
|
-
expect.stringContaining("Expected message"),
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Key Testing Patterns
|
|
60
|
-
|
|
61
|
-
### 1. Mocking Config.load
|
|
62
|
-
|
|
63
|
-
Always mock `Config.load` to return your test config. The handler loads config internally, so you can't pass it directly.
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
const mockConfig = await createTestConfig({
|
|
67
|
-
token: null,
|
|
68
|
-
workerId: null,
|
|
69
|
-
environment: "local",
|
|
70
|
-
baseUrl: "http://localhost:3000",
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 2. Creating Context
|
|
77
|
-
|
|
78
|
-
Handlers expect a context with specific properties:
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
function createBaseContext() {
|
|
82
|
-
return {
|
|
83
|
-
writer: new Writer({ debugEnabled: false }),
|
|
84
|
-
process,
|
|
85
|
-
fs,
|
|
86
|
-
os,
|
|
87
|
-
path,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### 3. Calling Handlers
|
|
93
|
-
|
|
94
|
-
Use `.call()` to provide the context:
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
await handler.call(context, flags, ...args);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Don't** call the handler directly without context - it will fail because `this.process` will be undefined.
|
|
101
|
-
|
|
102
|
-
### 4. Verifying Config Updates
|
|
103
|
-
|
|
104
|
-
Spy on config methods to verify they were called, rather than checking state directly:
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
const setTokenSpy = vi.spyOn(mockConfig, "setToken");
|
|
108
|
-
await handler.call(context, flags);
|
|
109
|
-
expect(setTokenSpy).toHaveBeenCalledWith(expectedValue);
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Config methods like `setToken()`, `setEnvironment()`, and `setWorkerId()` write to the file asynchronously, so checking the config state immediately won't work.
|
|
113
|
-
|
|
114
|
-
### 5. Mocking External Dependencies
|
|
115
|
-
|
|
116
|
-
Mock external modules before importing the implementation:
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
vi.mock("../utils/openUrl.js", () => ({
|
|
120
|
-
openNotionUrl: vi.fn().mockResolvedValue(undefined),
|
|
121
|
-
}));
|
|
122
|
-
|
|
123
|
-
import { commandHandler } from "./command.impl.js";
|
|
124
|
-
import { openNotionUrl } from "../utils/openUrl.js";
|
|
125
|
-
|
|
126
|
-
// In your test:
|
|
127
|
-
expect(openNotionUrl).toHaveBeenCalledWith("local", "http://...");
|
|
128
|
-
|
|
129
|
-
// For one-time failures:
|
|
130
|
-
vi.mocked(openNotionUrl).mockRejectedValueOnce(new Error("Failed"));
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 6. Capturing stdout/stderr
|
|
134
|
-
|
|
135
|
-
When testing output, spy on the write methods and check the content:
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
let stderrSpy: Mock<typeof process.stderr.write>;
|
|
139
|
-
|
|
140
|
-
beforeEach(() => {
|
|
141
|
-
stderrSpy = vi
|
|
142
|
-
.spyOn(process.stderr, "write")
|
|
143
|
-
.mockImplementation(() => true);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// For single messages:
|
|
147
|
-
expect(stderrSpy).toHaveBeenCalledWith(
|
|
148
|
-
expect.stringContaining("Expected message"),
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
// For multiple messages (concatenate all calls):
|
|
152
|
-
const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
|
|
153
|
-
expect(allCalls).toContain("Expected message");
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## Helper Functions
|
|
157
|
-
|
|
158
|
-
Place helper functions at the bottom of test files:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
// Test helpers
|
|
162
|
-
|
|
163
|
-
type ConfigFileContents = {
|
|
164
|
-
token: string | null;
|
|
165
|
-
workerId: string | null;
|
|
166
|
-
environment: Environment;
|
|
167
|
-
baseUrl: string;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const tmpDirectories: string[] = [];
|
|
171
|
-
|
|
172
|
-
const baseFlags: GlobalFlags = {
|
|
173
|
-
debug: false,
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
async function createConfigFile(contents: ConfigFileContents) {
|
|
177
|
-
const dir = await mkdtemp(path.join(tmpdir(), "test-prefix-"));
|
|
178
|
-
tmpDirectories.push(dir);
|
|
179
|
-
const configFilePath = path.join(dir, "config.json");
|
|
180
|
-
await writeFile(configFilePath, JSON.stringify(contents, null, 2), "utf-8");
|
|
181
|
-
return configFilePath;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function createTestConfig(
|
|
185
|
-
configFileContents: ConfigFileContents,
|
|
186
|
-
): Promise<Config> {
|
|
187
|
-
const configFilePath = await createConfigFile(configFileContents);
|
|
188
|
-
return Config.load({
|
|
189
|
-
configFilePath,
|
|
190
|
-
processEnv: {} as NodeJS.ProcessEnv,
|
|
191
|
-
flags: { debug: false },
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function createBaseContext() {
|
|
196
|
-
return {
|
|
197
|
-
writer: new Writer({ debugEnabled: false }),
|
|
198
|
-
process,
|
|
199
|
-
fs,
|
|
200
|
-
os,
|
|
201
|
-
path,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Common Mistakes to Avoid
|
|
207
|
-
|
|
208
|
-
1. **Don't call handlers directly without context**: Always use `.call(context, ...)`
|
|
209
|
-
2. **Don't check config state directly**: Spy on the setter methods instead
|
|
210
|
-
3. **Don't forget to mock Config.load**: The handler loads config internally
|
|
211
|
-
4. **Don't forget to create unique temp directories**: Use `mkdtemp` with unique prefixes
|
|
212
|
-
5. **Don't forget to clean up temp files**: Use `afterEach` to remove temp directories
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
afterEach,
|
|
3
|
-
beforeEach,
|
|
4
|
-
describe,
|
|
5
|
-
expect,
|
|
6
|
-
it,
|
|
7
|
-
type Mock,
|
|
8
|
-
vi,
|
|
9
|
-
} from "vitest";
|
|
10
|
-
import { Config } from "../config.js";
|
|
11
|
-
import {
|
|
12
|
-
baseFlags,
|
|
13
|
-
cleanupTmpDirectories,
|
|
14
|
-
createAndLoadConfig,
|
|
15
|
-
createBaseContext,
|
|
16
|
-
} from "./utils/testing.js";
|
|
17
|
-
|
|
18
|
-
// Mock the openUrl module before importing the implementation
|
|
19
|
-
vi.mock("../utils/openUrl.js", () => ({
|
|
20
|
-
openNotionUrl: vi.fn().mockResolvedValue(undefined),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
import { openNotionUrl } from "../utils/openUrl.js";
|
|
24
|
-
import { login, logout, show } from "./auth.impl.js";
|
|
25
|
-
|
|
26
|
-
afterEach(cleanupTmpDirectories);
|
|
27
|
-
|
|
28
|
-
describe("login", () => {
|
|
29
|
-
let stderrSpy: Mock<typeof process.stderr.write>;
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
stderrSpy = vi
|
|
33
|
-
.spyOn(process.stderr, "write")
|
|
34
|
-
.mockImplementation(() => true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("opens browser and displays instructions when no token provided", async () => {
|
|
38
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
39
|
-
configFile: {
|
|
40
|
-
token: null,
|
|
41
|
-
workerId: null,
|
|
42
|
-
environment: "local",
|
|
43
|
-
baseUrl: "http://localhost:3000",
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
48
|
-
|
|
49
|
-
const context = createBaseContext();
|
|
50
|
-
|
|
51
|
-
await login.call(context, baseFlags);
|
|
52
|
-
|
|
53
|
-
expect(openNotionUrl).toHaveBeenCalledWith(
|
|
54
|
-
"local",
|
|
55
|
-
"http://localhost:3000/__workers__?createToken=true",
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
|
|
59
|
-
expect(allCalls).toContain("Opening browser to create a token...");
|
|
60
|
-
expect(allCalls).toContain(
|
|
61
|
-
"http://localhost:3000/__workers__?createToken=true",
|
|
62
|
-
);
|
|
63
|
-
expect(allCalls).toContain("After creating a token, run:");
|
|
64
|
-
expect(allCalls).toContain("npx workers auth login <token>");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("shows error message when browser fails to open", async () => {
|
|
68
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
69
|
-
configFile: {
|
|
70
|
-
token: null,
|
|
71
|
-
workerId: null,
|
|
72
|
-
environment: "local",
|
|
73
|
-
baseUrl: "http://localhost:3000",
|
|
74
|
-
},
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
78
|
-
vi.mocked(openNotionUrl).mockRejectedValueOnce(new Error("Failed to open"));
|
|
79
|
-
|
|
80
|
-
const context = createBaseContext();
|
|
81
|
-
|
|
82
|
-
await login.call(context, baseFlags);
|
|
83
|
-
|
|
84
|
-
const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
|
|
85
|
-
expect(allCalls).toContain("Failed to open browser automatically");
|
|
86
|
-
expect(allCalls).toContain(
|
|
87
|
-
"http://localhost:3000/__workers__?createToken=true",
|
|
88
|
-
);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("saves token and clears workerId when token is provided", async () => {
|
|
92
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
93
|
-
configFile: {
|
|
94
|
-
token: null,
|
|
95
|
-
workerId: null,
|
|
96
|
-
environment: "local",
|
|
97
|
-
baseUrl: "http://localhost:3000",
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Spy on Config.load to return our mock config
|
|
102
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
103
|
-
|
|
104
|
-
const testToken =
|
|
105
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
|
|
106
|
-
|
|
107
|
-
const updateSpy = vi.spyOn(mockConfig, "update");
|
|
108
|
-
|
|
109
|
-
const context = createBaseContext();
|
|
110
|
-
|
|
111
|
-
await login.call(context, baseFlags, testToken);
|
|
112
|
-
|
|
113
|
-
expect(updateSpy).toHaveBeenCalledWith({
|
|
114
|
-
environment: "local",
|
|
115
|
-
token: testToken,
|
|
116
|
-
workerId: null,
|
|
117
|
-
});
|
|
118
|
-
expect(stderrSpy).toHaveBeenCalledWith(
|
|
119
|
-
expect.stringContaining("Successfully logged in!"),
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("saves environment from config when logging in", async () => {
|
|
124
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
125
|
-
configFile: {
|
|
126
|
-
token: null,
|
|
127
|
-
workerId: null,
|
|
128
|
-
baseUrl: "https://www.notion.so",
|
|
129
|
-
},
|
|
130
|
-
flags: {
|
|
131
|
-
env: "local",
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
136
|
-
|
|
137
|
-
const testToken =
|
|
138
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
|
|
139
|
-
|
|
140
|
-
const updateSpy = vi.spyOn(mockConfig, "update");
|
|
141
|
-
|
|
142
|
-
const context = createBaseContext();
|
|
143
|
-
|
|
144
|
-
await login.call(context, baseFlags, testToken);
|
|
145
|
-
|
|
146
|
-
expect(updateSpy).toHaveBeenCalledWith({
|
|
147
|
-
token: testToken,
|
|
148
|
-
workerId: null,
|
|
149
|
-
environment: "local",
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe("show", () => {
|
|
155
|
-
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
156
|
-
|
|
157
|
-
beforeEach(() => {
|
|
158
|
-
stdoutSpy = vi
|
|
159
|
-
.spyOn(process.stdout, "write")
|
|
160
|
-
.mockImplementation(() => true);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("writes token to stdout when token exists", async () => {
|
|
164
|
-
const testToken =
|
|
165
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig";
|
|
166
|
-
|
|
167
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
168
|
-
configFile: {
|
|
169
|
-
token: testToken,
|
|
170
|
-
workerId: "worker-1",
|
|
171
|
-
environment: "local",
|
|
172
|
-
baseUrl: "http://localhost:3000",
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
177
|
-
|
|
178
|
-
const context = createBaseContext();
|
|
179
|
-
|
|
180
|
-
await show.call(context, baseFlags);
|
|
181
|
-
|
|
182
|
-
expect(stdoutSpy).toHaveBeenCalledWith(`${testToken}\n`);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("writes empty string to stdout when no token exists", async () => {
|
|
186
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
187
|
-
configFile: {
|
|
188
|
-
token: null,
|
|
189
|
-
workerId: null,
|
|
190
|
-
environment: "local",
|
|
191
|
-
baseUrl: "http://localhost:3000",
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
196
|
-
|
|
197
|
-
const context = createBaseContext();
|
|
198
|
-
|
|
199
|
-
await show.call(context, baseFlags);
|
|
200
|
-
|
|
201
|
-
expect(stdoutSpy).toHaveBeenCalledWith("\n");
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe("logout", () => {
|
|
206
|
-
it("calls setToken with null", async () => {
|
|
207
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
208
|
-
configFile: {
|
|
209
|
-
token: "existing-token",
|
|
210
|
-
workerId: "worker-1",
|
|
211
|
-
environment: "local",
|
|
212
|
-
baseUrl: "http://localhost:3000",
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
217
|
-
|
|
218
|
-
const updateSpy = vi.spyOn(mockConfig, "update");
|
|
219
|
-
|
|
220
|
-
const context = createBaseContext();
|
|
221
|
-
|
|
222
|
-
await logout.call(context, baseFlags);
|
|
223
|
-
|
|
224
|
-
expect(updateSpy).toHaveBeenCalledWith({
|
|
225
|
-
token: null,
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import type { ConfigMap } from "../config.js";
|
|
2
|
-
import type { GlobalFlags } from "../flags.js";
|
|
3
|
-
import { buildHandler, type HandlerContext } from "../handler.js";
|
|
4
|
-
import { openNotionUrl } from "../utils/openUrl.js";
|
|
5
|
-
|
|
6
|
-
export const login = buildHandler(async function (
|
|
7
|
-
this: HandlerContext,
|
|
8
|
-
_: GlobalFlags,
|
|
9
|
-
token?: string,
|
|
10
|
-
) {
|
|
11
|
-
const environment = this.config.environment;
|
|
12
|
-
|
|
13
|
-
// If no token provided, open the browser to get one
|
|
14
|
-
if (!token) {
|
|
15
|
-
const url = `${this.config.baseUrl}/__workers__?createToken=true`;
|
|
16
|
-
|
|
17
|
-
this.io.writeErr(`Opening browser to create a token...\n${url}\n`);
|
|
18
|
-
this.io.writeErr("After creating a token, run:");
|
|
19
|
-
this.io.writeErr("\tnpx workers auth login <token>");
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
await openNotionUrl(environment, url);
|
|
23
|
-
} catch (_error) {
|
|
24
|
-
this.io.writeErr(
|
|
25
|
-
`Failed to open browser automatically. Please visit:\n ${url}`,
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const update: Partial<ConfigMap> = {
|
|
33
|
-
token,
|
|
34
|
-
workerId: null,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
console.log("environment", environment);
|
|
38
|
-
|
|
39
|
-
if (environment !== "prod") {
|
|
40
|
-
update.environment = environment;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
await this.config.update(update);
|
|
44
|
-
|
|
45
|
-
this.io.writeErr("Successfully logged in!");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
export const show = buildHandler(function (this: HandlerContext) {
|
|
49
|
-
this.io.writeOut(`${this.config.token ?? ""}`);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
export const logout = buildHandler(async function (this: HandlerContext) {
|
|
53
|
-
await this.config.update({
|
|
54
|
-
token: null,
|
|
55
|
-
});
|
|
56
|
-
});
|
package/src/cli/commands/auth.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
2
|
-
import { globalFlags } from "../flags.js";
|
|
3
|
-
|
|
4
|
-
export const authCommands = buildRouteMap({
|
|
5
|
-
docs: {
|
|
6
|
-
brief: "Commands for managing the CLI context",
|
|
7
|
-
},
|
|
8
|
-
routes: {
|
|
9
|
-
login: buildCommand({
|
|
10
|
-
docs: {
|
|
11
|
-
brief:
|
|
12
|
-
"Login to the Project Ajax platform using a Workers API token; will clear existing token, environment, and worker ID",
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
parameters: {
|
|
16
|
-
positional: {
|
|
17
|
-
kind: "tuple",
|
|
18
|
-
parameters: [
|
|
19
|
-
{
|
|
20
|
-
brief: "A Workers API token",
|
|
21
|
-
parse: String,
|
|
22
|
-
placeholder: "api-token",
|
|
23
|
-
optional: true,
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
flags: {
|
|
29
|
-
...globalFlags,
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
loader: () => import("./auth.impl.js").then((m) => m.login),
|
|
34
|
-
}),
|
|
35
|
-
|
|
36
|
-
show: buildCommand({
|
|
37
|
-
docs: {
|
|
38
|
-
brief: "Get the token for the CLI",
|
|
39
|
-
},
|
|
40
|
-
parameters: {
|
|
41
|
-
flags: {
|
|
42
|
-
...globalFlags,
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
loader: () => import("./auth.impl.js").then((m) => m.show),
|
|
47
|
-
}),
|
|
48
|
-
|
|
49
|
-
logout: buildCommand({
|
|
50
|
-
docs: {
|
|
51
|
-
brief: "Unset the token for the CLI",
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
parameters: {
|
|
55
|
-
flags: {
|
|
56
|
-
...globalFlags,
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
loader: () => import("./auth.impl.js").then((m) => m.logout),
|
|
61
|
-
}),
|
|
62
|
-
},
|
|
63
|
-
});
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
afterEach,
|
|
3
|
-
beforeEach,
|
|
4
|
-
describe,
|
|
5
|
-
expect,
|
|
6
|
-
it,
|
|
7
|
-
type Mock,
|
|
8
|
-
vi,
|
|
9
|
-
} from "vitest";
|
|
10
|
-
import { ApiClient } from "../api/client.js";
|
|
11
|
-
import { Result } from "../api/result.js";
|
|
12
|
-
import { Config } from "../config.js";
|
|
13
|
-
import { downloadBundle } from "./bundle.impl.js";
|
|
14
|
-
import {
|
|
15
|
-
baseFlags,
|
|
16
|
-
cleanupTmpDirectories,
|
|
17
|
-
createAndLoadConfig,
|
|
18
|
-
createBaseContext,
|
|
19
|
-
} from "./utils/testing.js";
|
|
20
|
-
|
|
21
|
-
afterEach(cleanupTmpDirectories);
|
|
22
|
-
|
|
23
|
-
describe("downloadBundle", () => {
|
|
24
|
-
let stderrSpy: Mock<typeof process.stderr.write>;
|
|
25
|
-
let stdoutSpy: Mock<typeof process.stdout.write>;
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
stderrSpy = vi
|
|
29
|
-
.spyOn(process.stderr, "write")
|
|
30
|
-
.mockImplementation(() => true);
|
|
31
|
-
stdoutSpy = vi
|
|
32
|
-
.spyOn(process.stdout, "write")
|
|
33
|
-
.mockImplementation(() => true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("downloads and pipes bundle to stdout when workerId is set", async () => {
|
|
37
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
38
|
-
configFile: {
|
|
39
|
-
token:
|
|
40
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
|
|
41
|
-
workerId: "worker-123",
|
|
42
|
-
environment: "local",
|
|
43
|
-
baseUrl: "http://localhost:3000",
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
48
|
-
|
|
49
|
-
// Create a mock web stream
|
|
50
|
-
const mockWebStream = new ReadableStream({
|
|
51
|
-
start(controller) {
|
|
52
|
-
controller.enqueue(new TextEncoder().encode("bundle content"));
|
|
53
|
-
controller.close();
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// This allows us to wait for the pipe to happen.
|
|
58
|
-
let resolveWrite: () => void;
|
|
59
|
-
const writePromise = new Promise<void>((resolve) => {
|
|
60
|
-
resolveWrite = resolve;
|
|
61
|
-
});
|
|
62
|
-
stdoutSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => {
|
|
63
|
-
resolveWrite();
|
|
64
|
-
return true;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const mockDownloadWorkerBundle = vi
|
|
68
|
-
.spyOn(ApiClient.prototype, "downloadWorkerBundle")
|
|
69
|
-
.mockResolvedValue(Result.success(mockWebStream));
|
|
70
|
-
|
|
71
|
-
const context = createBaseContext();
|
|
72
|
-
|
|
73
|
-
await downloadBundle.call(context, baseFlags);
|
|
74
|
-
|
|
75
|
-
expect(mockDownloadWorkerBundle).toHaveBeenCalledWith("worker-123");
|
|
76
|
-
await writePromise;
|
|
77
|
-
expect(stdoutSpy).toHaveBeenCalledWith(Buffer.from("bundle content"));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("throws error when workerId is not set", async () => {
|
|
81
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
82
|
-
configFile: {
|
|
83
|
-
token:
|
|
84
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
|
|
85
|
-
workerId: null,
|
|
86
|
-
environment: "local",
|
|
87
|
-
baseUrl: "http://localhost:3000",
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
92
|
-
|
|
93
|
-
const mockDownloadWorkerBundle = vi.spyOn(
|
|
94
|
-
ApiClient.prototype,
|
|
95
|
-
"downloadWorkerBundle",
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const context = createBaseContext();
|
|
99
|
-
|
|
100
|
-
await expect(downloadBundle.call(context, baseFlags)).rejects.toThrow(
|
|
101
|
-
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
expect(mockDownloadWorkerBundle).not.toHaveBeenCalled();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("handles download failure gracefully", async () => {
|
|
108
|
-
const [mockConfig] = await createAndLoadConfig({
|
|
109
|
-
configFile: {
|
|
110
|
-
token:
|
|
111
|
-
"1.2.eyJzcGFjZUlkIjoic3BhY2UxIiwidXNlcklkIjoidXNlcjEiLCJjZWxsSWQiOiJjZWxsMSJ9.sig",
|
|
112
|
-
workerId: "worker-123",
|
|
113
|
-
environment: "local",
|
|
114
|
-
baseUrl: "http://localhost:3000",
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
vi.spyOn(Config, "load").mockResolvedValue(mockConfig);
|
|
119
|
-
|
|
120
|
-
const mockDownloadWorkerBundle = vi
|
|
121
|
-
.spyOn(ApiClient.prototype, "downloadWorkerBundle")
|
|
122
|
-
.mockResolvedValue(
|
|
123
|
-
Result.fail({
|
|
124
|
-
status: 404,
|
|
125
|
-
statusText: "Not Found",
|
|
126
|
-
message: "Worker bundle not found",
|
|
127
|
-
}),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const context = createBaseContext();
|
|
131
|
-
|
|
132
|
-
await downloadBundle.call(context, baseFlags);
|
|
133
|
-
|
|
134
|
-
expect(mockDownloadWorkerBundle).toHaveBeenCalledWith("worker-123");
|
|
135
|
-
|
|
136
|
-
const allCalls = stderrSpy.mock.calls.map((call) => call[0]).join("");
|
|
137
|
-
expect(allCalls).toContain("✗ Failed to download bundle");
|
|
138
|
-
expect(allCalls).toContain("✗ Worker bundle not found");
|
|
139
|
-
|
|
140
|
-
// Should not write anything to stdout on error
|
|
141
|
-
expect(stdoutSpy).not.toHaveBeenCalled();
|
|
142
|
-
});
|
|
143
|
-
});
|