@vellumai/assistant 0.4.43 → 0.4.44
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/ARCHITECTURE.md +13 -14
- package/README.md +11 -12
- package/docs/architecture/integrations.md +75 -93
- package/package.json +1 -1
- package/src/__tests__/approval-routes-http.test.ts +0 -2
- package/src/__tests__/bundled-asset.test.ts +1 -1
- package/src/__tests__/checker.test.ts +31 -28
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
- package/src/__tests__/managed-twitter-guardrails.test.ts +5 -1
- package/src/__tests__/onboarding-template-contract.test.ts +0 -10
- package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
- package/src/__tests__/send-endpoint-busy.test.ts +0 -3
- package/src/__tests__/session-confirmation-signals.test.ts +7 -45
- package/src/__tests__/starter-task-flow.test.ts +9 -19
- package/src/__tests__/system-prompt.test.ts +3 -4
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/twitter-platform-proxy-client.test.ts +43 -18
- package/src/cli/commands/amazon/index.ts +4 -39
- package/src/cli/commands/amazon/session.ts +18 -26
- package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +58 -196
- package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +26 -186
- package/src/cli/commands/twitter/__tests__/oauth-client.test.ts +1 -47
- package/src/cli/commands/twitter/index.ts +95 -835
- package/src/cli/commands/twitter/oauth-client.ts +1 -35
- package/src/cli/commands/twitter/router.ts +70 -115
- package/src/cli/commands/twitter/types.ts +30 -0
- package/src/cli/reference.ts +2 -2
- package/src/config/bundled-skills/amazon/SKILL.md +0 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -6
- package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
- package/src/config/bundled-skills/doordash/SKILL.md +0 -1
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -82
- package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -28
- package/src/config/bundled-skills/doordash/lib/session.ts +21 -17
- package/src/config/bundled-skills/twitter/SKILL.md +53 -166
- package/src/config/feature-flag-registry.json +8 -0
- package/src/daemon/handlers/session-history.ts +41 -9
- package/src/daemon/lifecycle.ts +4 -17
- package/src/daemon/message-types/apps.ts +0 -25
- package/src/daemon/message-types/integrations.ts +1 -7
- package/src/daemon/message-types/sessions.ts +6 -1
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/ride-shotgun-handler.ts +33 -1
- package/src/daemon/seed-files.ts +3 -27
- package/src/daemon/server.ts +2 -18
- package/src/daemon/session-agent-loop-handlers.ts +24 -2
- package/src/daemon/session-runtime-assembly.ts +0 -7
- package/src/daemon/session-surfaces.ts +185 -33
- package/src/daemon/session.ts +2 -28
- package/src/memory/app-store.ts +0 -18
- package/src/memory/schema/infrastructure.ts +0 -8
- package/src/permissions/defaults.ts +3 -3
- package/src/prompts/system-prompt.ts +4 -5
- package/src/prompts/templates/BOOTSTRAP.md +0 -3
- package/src/providers/registry.ts +2 -4
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +2 -1
- package/src/runtime/auth/route-policy.ts +0 -4
- package/src/runtime/auth/scopes.ts +1 -0
- package/src/runtime/auth/token-service.ts +1 -1
- package/src/runtime/http-types.ts +10 -0
- package/src/runtime/middleware/error-handler.ts +14 -1
- package/src/runtime/routes/app-management-routes.ts +61 -64
- package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
- package/src/runtime/routes/brain-graph-routes.ts +4 -42
- package/src/runtime/routes/conversation-routes.ts +9 -6
- package/src/runtime/routes/diagnostics-routes.ts +91 -14
- package/src/runtime/routes/settings-routes.ts +3 -93
- package/src/tools/AGENTS.md +38 -0
- package/src/tools/apps/executors.ts +0 -6
- package/src/tools/document/editor-template.ts +10 -8
- package/src/twitter/platform-proxy-client.ts +6 -3
- package/src/util/errors.ts +12 -0
- package/src/__tests__/home-base-bootstrap.test.ts +0 -84
- package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
- package/src/cli/commands/twitter/__tests__/cli-error-shaping.test.ts +0 -265
- package/src/cli/commands/twitter/client.ts +0 -989
- package/src/cli/commands/twitter/session.ts +0 -121
- package/src/home-base/app-link-store.ts +0 -78
- package/src/home-base/bootstrap.ts +0 -74
- package/src/home-base/prebuilt/brain-graph.html +0 -1483
- package/src/home-base/prebuilt/index.html +0 -702
- package/src/home-base/prebuilt/seed-metadata.json +0 -21
- package/src/home-base/prebuilt/seed.ts +0 -122
- package/src/home-base/prebuilt-home-base-updater.ts +0 -36
- package/src/util/cookie-session.ts +0 -98
|
@@ -8,19 +8,12 @@ let mockOauthPostResult: {
|
|
|
8
8
|
url?: string;
|
|
9
9
|
} | null = null;
|
|
10
10
|
let mockOauthPostError: Error | null = null;
|
|
11
|
-
let mockBrowserPostResult: {
|
|
12
|
-
tweetId: string;
|
|
13
|
-
text: string;
|
|
14
|
-
url: string;
|
|
15
|
-
} | null = null;
|
|
16
|
-
let mockBrowserPostError: Error | null = null;
|
|
17
11
|
let mockManagedPostResult: { data: unknown; status: number } | null = null;
|
|
18
12
|
let mockManagedPostError: Error | null = null;
|
|
19
13
|
|
|
20
14
|
// Mock the OAuth client
|
|
21
15
|
mock.module("../oauth-client.js", () => ({
|
|
22
16
|
oauthIsAvailable: (token?: string) => token != null && token.length > 0,
|
|
23
|
-
oauthSupportsOperation: (op: string) => op === "post" || op === "reply",
|
|
24
17
|
oauthPostTweet: async (
|
|
25
18
|
_text: string,
|
|
26
19
|
_opts: { inReplyToTweetId?: string; oauthToken: string },
|
|
@@ -29,16 +22,6 @@ mock.module("../oauth-client.js", () => ({
|
|
|
29
22
|
if (mockOauthPostResult) return mockOauthPostResult;
|
|
30
23
|
throw new Error("OAuth mock not configured");
|
|
31
24
|
},
|
|
32
|
-
UnsupportedOAuthOperationError: class UnsupportedOAuthOperationError extends Error {
|
|
33
|
-
public readonly suggestFallback = true;
|
|
34
|
-
public readonly fallbackPath = "browser" as const;
|
|
35
|
-
public readonly operation: string;
|
|
36
|
-
constructor(operation: string) {
|
|
37
|
-
super(`The "${operation}" operation is not available via the OAuth API.`);
|
|
38
|
-
this.name = "UnsupportedOAuthOperationError";
|
|
39
|
-
this.operation = operation;
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
25
|
}));
|
|
43
26
|
|
|
44
27
|
// Mock TwitterProxyError for managed path tests
|
|
@@ -70,24 +53,6 @@ mock.module("../../../../twitter/platform-proxy-client.js", () => ({
|
|
|
70
53
|
TwitterProxyError: MockTwitterProxyError,
|
|
71
54
|
}));
|
|
72
55
|
|
|
73
|
-
// Create a SessionExpiredError class that matches the real one
|
|
74
|
-
class MockSessionExpiredError extends Error {
|
|
75
|
-
constructor(reason: string) {
|
|
76
|
-
super(reason);
|
|
77
|
-
this.name = "SessionExpiredError";
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Mock the browser client
|
|
82
|
-
mock.module("../client.js", () => ({
|
|
83
|
-
postTweet: async (_text: string, _opts?: { inReplyToTweetId?: string }) => {
|
|
84
|
-
if (mockBrowserPostError) throw mockBrowserPostError;
|
|
85
|
-
if (mockBrowserPostResult) return mockBrowserPostResult;
|
|
86
|
-
throw new Error("Browser mock not configured");
|
|
87
|
-
},
|
|
88
|
-
SessionExpiredError: MockSessionExpiredError,
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
56
|
// Mock the logger to silence output
|
|
92
57
|
mock.module("../../../../util/logger.js", () => ({
|
|
93
58
|
getLogger: () => ({
|
|
@@ -111,114 +76,25 @@ import { routedPostTweet } from "../router.js";
|
|
|
111
76
|
beforeEach(() => {
|
|
112
77
|
mockOauthPostResult = null;
|
|
113
78
|
mockOauthPostError = null;
|
|
114
|
-
mockBrowserPostResult = null;
|
|
115
|
-
mockBrowserPostError = null;
|
|
116
79
|
mockManagedPostResult = null;
|
|
117
80
|
mockManagedPostError = null;
|
|
118
81
|
});
|
|
119
82
|
|
|
120
|
-
describe("Twitter
|
|
121
|
-
describe("
|
|
122
|
-
test("uses OAuth when available and supported", async () => {
|
|
123
|
-
mockOauthPostResult = {
|
|
124
|
-
tweetId: "111",
|
|
125
|
-
text: "hello",
|
|
126
|
-
url: "https://x.com/u/status/111",
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const { result, pathUsed } = await routedPostTweet("hello", {
|
|
130
|
-
strategy: "auto",
|
|
131
|
-
oauthToken: "test-token",
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
expect(pathUsed).toBe("oauth");
|
|
135
|
-
expect(result.tweetId).toBe("111");
|
|
136
|
-
expect(result.text).toBe("hello");
|
|
137
|
-
expect(result.url).toBe("https://x.com/u/status/111");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("falls back to browser when OAuth is unavailable", async () => {
|
|
141
|
-
mockBrowserPostResult = {
|
|
142
|
-
tweetId: "222",
|
|
143
|
-
text: "hello",
|
|
144
|
-
url: "https://x.com/u/status/222",
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const { result, pathUsed } = await routedPostTweet("hello", {
|
|
148
|
-
strategy: "auto",
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
expect(pathUsed).toBe("browser");
|
|
152
|
-
expect(result.tweetId).toBe("222");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("falls back to browser when OAuth fails", async () => {
|
|
156
|
-
mockOauthPostError = new Error("OAuth token expired");
|
|
157
|
-
mockBrowserPostResult = {
|
|
158
|
-
tweetId: "333",
|
|
159
|
-
text: "hello",
|
|
160
|
-
url: "https://x.com/u/status/333",
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const { result, pathUsed } = await routedPostTweet("hello", {
|
|
164
|
-
strategy: "auto",
|
|
165
|
-
oauthToken: "test-token",
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
expect(pathUsed).toBe("browser");
|
|
169
|
-
expect(result.tweetId).toBe("333");
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
test("constructs URL from tweetId when OAuth result has no url", async () => {
|
|
173
|
-
mockOauthPostResult = { tweetId: "444", text: "no url" };
|
|
174
|
-
|
|
175
|
-
const { result, pathUsed } = await routedPostTweet("no url", {
|
|
176
|
-
strategy: "auto",
|
|
177
|
-
oauthToken: "test-token",
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
expect(pathUsed).toBe("oauth");
|
|
181
|
-
expect(result.url).toBe("https://x.com/i/status/444");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test("throws combined error when both OAuth and browser fail with SessionExpiredError", async () => {
|
|
185
|
-
mockOauthPostError = new Error("OAuth failed");
|
|
186
|
-
mockBrowserPostError = new MockSessionExpiredError(
|
|
187
|
-
"Browser session expired",
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
await routedPostTweet("will fail", {
|
|
192
|
-
strategy: "auto",
|
|
193
|
-
oauthToken: "test-token",
|
|
194
|
-
});
|
|
195
|
-
expect(true).toBe(false); // should not reach
|
|
196
|
-
} catch (err) {
|
|
197
|
-
const e = err as Error & { pathUsed: string; oauthError?: string };
|
|
198
|
-
expect(e).toBeInstanceOf(MockSessionExpiredError);
|
|
199
|
-
expect(e.message).toBe("Browser session expired");
|
|
200
|
-
expect(e.pathUsed).toBe("auto");
|
|
201
|
-
expect(e.oauthError).toBe("OAuth failed");
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe("explicit oauth strategy", () => {
|
|
83
|
+
describe("Twitter mode router", () => {
|
|
84
|
+
describe("explicit oauth mode", () => {
|
|
207
85
|
test("fails with helpful error when OAuth is not configured", async () => {
|
|
208
86
|
try {
|
|
209
|
-
await routedPostTweet("hello", {
|
|
87
|
+
await routedPostTweet("hello", { mode: "oauth" });
|
|
210
88
|
expect(true).toBe(false); // should not reach
|
|
211
89
|
} catch (err) {
|
|
212
90
|
const e = err as Error & {
|
|
213
91
|
pathUsed: string;
|
|
214
|
-
suggestAlternative: string;
|
|
215
92
|
};
|
|
216
93
|
expect(e.message).toContain("OAuth is not configured");
|
|
217
94
|
expect(e.message).toContain(
|
|
218
|
-
"
|
|
95
|
+
"Connect your X developer credentials to set up OAuth",
|
|
219
96
|
);
|
|
220
97
|
expect(e.pathUsed).toBe("oauth");
|
|
221
|
-
expect(e.suggestAlternative).toBe("browser");
|
|
222
98
|
}
|
|
223
99
|
});
|
|
224
100
|
|
|
@@ -226,63 +102,28 @@ describe("Twitter strategy router", () => {
|
|
|
226
102
|
mockOauthPostResult = { tweetId: "555", text: "oauth post" };
|
|
227
103
|
|
|
228
104
|
const { result, pathUsed } = await routedPostTweet("oauth post", {
|
|
229
|
-
|
|
105
|
+
mode: "oauth",
|
|
230
106
|
oauthToken: "test-token",
|
|
231
107
|
});
|
|
232
108
|
|
|
233
109
|
expect(pathUsed).toBe("oauth");
|
|
234
110
|
expect(result.tweetId).toBe("555");
|
|
235
111
|
});
|
|
236
|
-
});
|
|
237
112
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
mockBrowserPostResult = {
|
|
241
|
-
tweetId: "666",
|
|
242
|
-
text: "browser post",
|
|
243
|
-
url: "https://x.com/u/status/666",
|
|
244
|
-
};
|
|
113
|
+
test("constructs URL from tweetId when OAuth result has no url", async () => {
|
|
114
|
+
mockOauthPostResult = { tweetId: "444", text: "no url" };
|
|
245
115
|
|
|
246
|
-
const { result, pathUsed } = await routedPostTweet("
|
|
247
|
-
|
|
248
|
-
oauthToken: "test-token",
|
|
116
|
+
const { result, pathUsed } = await routedPostTweet("no url", {
|
|
117
|
+
mode: "oauth",
|
|
118
|
+
oauthToken: "test-token",
|
|
249
119
|
});
|
|
250
120
|
|
|
251
|
-
expect(pathUsed).toBe("
|
|
252
|
-
expect(result.
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
test("preserves SessionExpiredError type with router metadata", async () => {
|
|
256
|
-
mockBrowserPostError = new MockSessionExpiredError("Session expired");
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
await routedPostTweet("will fail", { strategy: "browser" });
|
|
260
|
-
expect(true).toBe(false); // should not reach
|
|
261
|
-
} catch (err) {
|
|
262
|
-
const e = err as Error & {
|
|
263
|
-
pathUsed: string;
|
|
264
|
-
suggestAlternative: string;
|
|
265
|
-
};
|
|
266
|
-
expect(e).toBeInstanceOf(MockSessionExpiredError);
|
|
267
|
-
expect(e.message).toBe("Session expired");
|
|
268
|
-
expect(e.pathUsed).toBe("browser");
|
|
269
|
-
expect(e.suggestAlternative).toBe("oauth");
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test("re-throws non-session errors without wrapping", async () => {
|
|
274
|
-
mockBrowserPostError = new Error("Network failure");
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
await routedPostTweet("will fail", { strategy: "browser" });
|
|
278
|
-
expect(true).toBe(false); // should not reach
|
|
279
|
-
} catch (err) {
|
|
280
|
-
expect((err as Error).message).toBe("Network failure");
|
|
281
|
-
}
|
|
121
|
+
expect(pathUsed).toBe("oauth");
|
|
122
|
+
expect(result.url).toBe("https://x.com/i/status/444");
|
|
282
123
|
});
|
|
283
124
|
});
|
|
284
125
|
|
|
285
|
-
describe("managed
|
|
126
|
+
describe("managed mode", () => {
|
|
286
127
|
test("routes post through platform proxy", async () => {
|
|
287
128
|
mockManagedPostResult = {
|
|
288
129
|
data: { data: { id: "managed-1" } },
|
|
@@ -290,7 +131,7 @@ describe("Twitter strategy router", () => {
|
|
|
290
131
|
};
|
|
291
132
|
|
|
292
133
|
const { result, pathUsed } = await routedPostTweet("managed post", {
|
|
293
|
-
|
|
134
|
+
mode: "managed",
|
|
294
135
|
});
|
|
295
136
|
|
|
296
137
|
expect(pathUsed).toBe("managed");
|
|
@@ -305,7 +146,7 @@ describe("Twitter strategy router", () => {
|
|
|
305
146
|
};
|
|
306
147
|
|
|
307
148
|
const { result, pathUsed } = await routedPostTweet("reply text", {
|
|
308
|
-
|
|
149
|
+
mode: "managed",
|
|
309
150
|
inReplyToTweetId: "original-tweet-123",
|
|
310
151
|
});
|
|
311
152
|
|
|
@@ -322,7 +163,7 @@ describe("Twitter strategy router", () => {
|
|
|
322
163
|
);
|
|
323
164
|
|
|
324
165
|
try {
|
|
325
|
-
await routedPostTweet("will fail", {
|
|
166
|
+
await routedPostTweet("will fail", { mode: "managed" });
|
|
326
167
|
expect(true).toBe(false); // should not reach
|
|
327
168
|
} catch (err) {
|
|
328
169
|
const e = err as Error & {
|
|
@@ -348,7 +189,7 @@ describe("Twitter strategy router", () => {
|
|
|
348
189
|
);
|
|
349
190
|
|
|
350
191
|
try {
|
|
351
|
-
await routedPostTweet("will fail", {
|
|
192
|
+
await routedPostTweet("will fail", { mode: "managed" });
|
|
352
193
|
expect(true).toBe(false);
|
|
353
194
|
} catch (err) {
|
|
354
195
|
const e = err as Error & {
|
|
@@ -366,7 +207,7 @@ describe("Twitter strategy router", () => {
|
|
|
366
207
|
mockManagedPostError = new Error("Network failure");
|
|
367
208
|
|
|
368
209
|
try {
|
|
369
|
-
await routedPostTweet("will fail", {
|
|
210
|
+
await routedPostTweet("will fail", { mode: "managed" });
|
|
370
211
|
expect(true).toBe(false);
|
|
371
212
|
} catch (err) {
|
|
372
213
|
expect((err as Error).message).toBe("Network failure");
|
|
@@ -376,7 +217,7 @@ describe("Twitter strategy router", () => {
|
|
|
376
217
|
});
|
|
377
218
|
|
|
378
219
|
describe("reply routing", () => {
|
|
379
|
-
test("
|
|
220
|
+
test("oauth mode routes reply through OAuth when available", async () => {
|
|
380
221
|
mockOauthPostResult = {
|
|
381
222
|
tweetId: "777",
|
|
382
223
|
text: "reply text",
|
|
@@ -385,7 +226,7 @@ describe("Twitter strategy router", () => {
|
|
|
385
226
|
|
|
386
227
|
const { result, pathUsed } = await routedPostTweet("reply text", {
|
|
387
228
|
inReplyToTweetId: "100",
|
|
388
|
-
|
|
229
|
+
mode: "oauth",
|
|
389
230
|
oauthToken: "test-token",
|
|
390
231
|
});
|
|
391
232
|
|
|
@@ -393,19 +234,18 @@ describe("Twitter strategy router", () => {
|
|
|
393
234
|
expect(result.tweetId).toBe("777");
|
|
394
235
|
});
|
|
395
236
|
|
|
396
|
-
test("
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
url: "https://x.com/u/status/888",
|
|
237
|
+
test("managed mode routes reply through platform proxy", async () => {
|
|
238
|
+
mockManagedPostResult = {
|
|
239
|
+
data: { data: { id: "888" } },
|
|
240
|
+
status: 200,
|
|
401
241
|
};
|
|
402
242
|
|
|
403
243
|
const { result, pathUsed } = await routedPostTweet("reply text", {
|
|
404
244
|
inReplyToTweetId: "200",
|
|
405
|
-
|
|
245
|
+
mode: "managed",
|
|
406
246
|
});
|
|
407
247
|
|
|
408
|
-
expect(pathUsed).toBe("
|
|
248
|
+
expect(pathUsed).toBe("managed");
|
|
409
249
|
expect(result.tweetId).toBe("888");
|
|
410
250
|
});
|
|
411
251
|
});
|
|
@@ -19,12 +19,7 @@ mock.module("../../../../util/logger.js", () => ({
|
|
|
19
19
|
}),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
import {
|
|
23
|
-
oauthIsAvailable,
|
|
24
|
-
oauthPostTweet,
|
|
25
|
-
oauthSupportsOperation,
|
|
26
|
-
UnsupportedOAuthOperationError,
|
|
27
|
-
} from "../oauth-client.js";
|
|
22
|
+
import { oauthIsAvailable, oauthPostTweet } from "../oauth-client.js";
|
|
28
23
|
|
|
29
24
|
// --- Global fetch mock ---
|
|
30
25
|
|
|
@@ -153,45 +148,4 @@ describe("Twitter OAuth client", () => {
|
|
|
153
148
|
expect(oauthIsAvailable("")).toBe(false);
|
|
154
149
|
});
|
|
155
150
|
});
|
|
156
|
-
|
|
157
|
-
describe("oauthSupportsOperation", () => {
|
|
158
|
-
test("returns true for post", () => {
|
|
159
|
-
expect(oauthSupportsOperation("post")).toBe(true);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("returns true for reply", () => {
|
|
163
|
-
expect(oauthSupportsOperation("reply")).toBe(true);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("returns false for unsupported operations", () => {
|
|
167
|
-
const unsupported = [
|
|
168
|
-
"timeline",
|
|
169
|
-
"search",
|
|
170
|
-
"bookmarks",
|
|
171
|
-
"home",
|
|
172
|
-
"notifications",
|
|
173
|
-
"likes",
|
|
174
|
-
"followers",
|
|
175
|
-
"following",
|
|
176
|
-
"media",
|
|
177
|
-
"tweet",
|
|
178
|
-
];
|
|
179
|
-
for (const op of unsupported) {
|
|
180
|
-
expect(oauthSupportsOperation(op)).toBe(false);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe("UnsupportedOAuthOperationError", () => {
|
|
186
|
-
test("has correct properties", () => {
|
|
187
|
-
const err = new UnsupportedOAuthOperationError("search");
|
|
188
|
-
expect(err.name).toBe("UnsupportedOAuthOperationError");
|
|
189
|
-
expect(err.operation).toBe("search");
|
|
190
|
-
expect(err.suggestFallback).toBe(true);
|
|
191
|
-
expect(err.fallbackPath).toBe("browser");
|
|
192
|
-
expect(err.message).toContain("search");
|
|
193
|
-
expect(err.message).toContain("not available via the OAuth API");
|
|
194
|
-
expect(err).toBeInstanceOf(Error);
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
151
|
});
|