Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/forward/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class Schedules extends APIResource {
return this._client.request<ScheduleArchiveManyResponse>({
method: "POST",
path,
body: payload,
body: { ...payload, scope: "by_schedule_ids" },
...options,
headers: requestHeaders({ "Idempotency-Key": idempotency_key }, options?.headers),
});
Expand Down
3 changes: 2 additions & 1 deletion tests/fixtures/forward/api-operation-cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@
"value": {
"schedule_ids": [
"sdk-contract"
]
],
"scope": "by_schedule_ids"
}
}
},
Expand Down
8 changes: 6 additions & 2 deletions tests/live/forward-scenarios.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ scenario('environment_lifecycle', async (s) => {
scenario('session_resource_thread_lifecycle', async (s) => {
const identity = await s.identity();
const template = await s.template((await s.environment()).id);
// Create the file first so reverse-order cleanup archives the session before deleting its mounted file.
const file = await s.file('sdk-resource.txt', 'session_resource', 'SDK resource');
const session = await s.session(identity.id, template.id);
assert.equal((await s.client.sessions.update(session.id, { title: 'SDK updated session' }, s.options)).title, 'SDK updated session');
const file = await s.file('sdk-resource.txt', 'session_resource', 'SDK resource');
const resource = await s.client.sessions.resources.add(session.id, { type: 'file', file_id: file.id, mount_path: '/data/workspace/sdk-resource.txt' }, s.options);
assert.equal(resource.file_id, file.id);
assert.equal(resource.mount_path, '/data/workspace/sdk-resource.txt');
Expand Down Expand Up @@ -194,7 +195,10 @@ scenario('schedule_lifecycle', async (s) => {
const template = await s.template(environment.id);
const schedule = await s.client.schedules.create({ identity_id: identity.id, template_id: template.id, environment_id: environment.id,
name: liveName('schedule'), initial_events: [{ type: 'user.message', content: 'Reply with SDK-LIVE.' }], trigger_policy: { type: 'manual' } }, s.options);
s.cleanup('schedule', (options) => s.client.schedules.archive(schedule.id, {}, options));
s.cleanup('schedule', async (options) => {
const current = await s.client.schedules.retrieve(schedule.id, options);
if (!current.archived_at) await s.client.schedules.archive(schedule.id, {}, options);
});
assert.equal((await s.client.schedules.retrieve(schedule.id, s.options)).id, schedule.id);
assert.equal((await s.client.schedules.update(schedule.id, { description: 'SDK updated schedule' }, s.options)).description, 'SDK updated schedule');
assert.equal((await s.client.schedules.pause(schedule.id, {}, s.options)).status, 'paused');
Expand Down
2 changes: 1 addition & 1 deletion tests/live/managed-scenarios.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const managedScenarios = [
assert((await download.arrayBuffer()).byteLength > 0, 'Empty skill archive');
}),
scenario('environment_lifecycle', 'write', async (s) => {
const environment = await s.createEnvironment();
const environment = await s.createEnvironment({ type: 'self_hosted' });
const got = await s.client.environments.retrieve(environment.id, {}, s.options());
assert.equal(got.id, environment.id);
await s.client.environments.update(environment.id, { description: 'updated through Managed TypeScript SDK' }, s.options());
Expand Down
4 changes: 2 additions & 2 deletions tests/live/managed-support.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export class ManagedScenarioSuite {
if (!model) throw new ScenarioSkip('The Managed account has no enabled model');
return model.id;
}
async createEnvironment() {
const environment = await this.client.environments.create({ name: unique('env'), config: { type: 'cloud' }, metadata: { suite: 'sdk-live' } }, this.options());
async createEnvironment(config = { type: 'cloud' }) {
const environment = await this.client.environments.create({ name: unique('env'), config, metadata: { suite: 'sdk-live' } }, this.options());
this.cleanup(`Environment ${environment.id}`, async () => {
try { await this.client.environments.delete(environment.id, {}, this.options()); }
catch (error) {
Expand Down
18 changes: 16 additions & 2 deletions tests/scenario-harness.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ export class MockPlatform {
}
if (action === 'effective') return response(this.records.get(parent + '/config') ?? {});
if (parts[0] === 'qr_sessions') return response(this.get(path));
if (parts[0] === 'environments' && action === 'work' && this.get(parent).config?.type !== 'self_hosted') {
return response({ error: { message: 'environment is not self-hosted' } }, 400);
}
if (route.method === 'list' || route.method === 'listTemplates') {
let values = this.data(path);
for (const field of ['identity_id','template_id','schedule_id','status']) if (url.searchParams.has(field)) values = values.filter(v => String(v[field]) === url.searchParams.get(field));
Expand All @@ -173,9 +176,20 @@ export class MockPlatform {
if (!this.records.has(path)) return response({ error: { message: 'not found' } }, 404);
return response(clone(this.get(path)));
}
if (req.method === 'DELETE') { const item = this.get(path); this.delete(path); return response({ id: item.id, deleted: true }); }
if (req.method === 'DELETE') {
if (parts[0] === 'files' && parts.length === 2) {
const mounted = [...this.records].some(([key, resource]) => {
const match = key.match(/^\/sessions\/([^/]+)\/resources\//);
return match && resource.file_id === parts[1] && !terminal.has(this.records.get(`/sessions/${match[1]}`)?.status);
});
if (mounted) return response({ error: { message: 'file is mounted by an active session' } }, 409);
}
const item = this.get(path); this.delete(path); return response({ id: item.id, deleted: true });
}
if (action === 'archive' && parts.length === 2 && parts[0] === 'schedules') {
const ids = [...new Set(body.schedule_ids)]; for (const id of ids) this.get(`/schedules/${id}`).status = 'archived';
assert.equal(body.scope, 'by_schedule_ids', 'schedule archive scope');
const ids = [...new Set(body.schedule_ids)];
for (const id of ids) Object.assign(this.get(`/schedules/${id}`), { status: 'archived', archived_at: new Date(0).toISOString() });
return response({ archived_count: ids.length });
}
if (['archive','pause','unpause','disable','enable','cancel'].includes(action)) {
Expand Down
Loading