Add features & update project
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { convexTest } from 'convex-test';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import type { Id } from '../../convex/_generated/dataModel.js';
|
||||
import { api } from '../../convex/_generated/api.js';
|
||||
import schema from '../../convex/schema';
|
||||
|
||||
@@ -33,6 +34,60 @@ const spoonInput = {
|
||||
productionRefStrategy: 'default_branch' as const,
|
||||
};
|
||||
|
||||
const createAgentJob = async (
|
||||
t: ReturnType<typeof convexTest>,
|
||||
args: {
|
||||
ownerId: Id<'users'>;
|
||||
spoonId: Id<'spoons'>;
|
||||
status: 'running' | 'failed' | 'cancelled';
|
||||
workspaceStatus?: 'active' | 'stopped' | 'failed' | 'expired';
|
||||
},
|
||||
) =>
|
||||
await t.mutation(async (ctx) => {
|
||||
const now = Date.now();
|
||||
const requestId = await ctx.db.insert('agentRequests', {
|
||||
spoonId: args.spoonId,
|
||||
ownerId: args.ownerId,
|
||||
prompt: 'Clean this workspace',
|
||||
status: 'running',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
const jobId = await ctx.db.insert('agentJobs', {
|
||||
spoonId: args.spoonId,
|
||||
ownerId: args.ownerId,
|
||||
agentRequestId: requestId,
|
||||
status: args.status,
|
||||
prompt: 'Clean this workspace',
|
||||
runtime: 'opencode',
|
||||
workspaceStatus: args.workspaceStatus,
|
||||
baseBranch: 'main',
|
||||
workBranch: 'spoon/test',
|
||||
forkOwner: 'team',
|
||||
forkRepo: 'editor-spoon',
|
||||
forkUrl: 'https://git.example.com/team/editor-spoon',
|
||||
upstreamOwner: 'upstream',
|
||||
upstreamRepo: 'editor',
|
||||
selectedSecretIds: [],
|
||||
model: 'openai/gpt-5.1-codex',
|
||||
reasoningEffort: 'medium',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
await ctx.db.patch(requestId, { agentJobId: jobId });
|
||||
await ctx.db.insert('agentJobMessages', {
|
||||
jobId,
|
||||
spoonId: args.spoonId,
|
||||
ownerId: args.ownerId,
|
||||
role: 'assistant',
|
||||
content: 'done',
|
||||
status: 'completed',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
return jobId;
|
||||
});
|
||||
|
||||
describe('convex-test harness', () => {
|
||||
test('boots and executes against the project schema', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
@@ -89,4 +144,71 @@ describe('convex-test harness', () => {
|
||||
}),
|
||||
).rejects.toThrow('Spoon not found.');
|
||||
});
|
||||
|
||||
test('deletes terminal workspaces and associated rows', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
|
||||
const spoonId = await authed(t, ownerId).mutation(
|
||||
api.spoons.createManual,
|
||||
spoonInput,
|
||||
);
|
||||
const jobId = await createAgentJob(t, {
|
||||
ownerId,
|
||||
spoonId,
|
||||
status: 'failed',
|
||||
workspaceStatus: 'failed',
|
||||
});
|
||||
|
||||
await authed(t, ownerId).mutation(api.agentJobs.deleteWorkspace, { jobId });
|
||||
|
||||
const job = await t.run(async (ctx) => await ctx.db.get(jobId));
|
||||
const messages = await t.run(
|
||||
async (ctx) =>
|
||||
await ctx.db
|
||||
.query('agentJobMessages')
|
||||
.withIndex('by_job', (q) => q.eq('jobId', jobId))
|
||||
.collect(),
|
||||
);
|
||||
expect(job).toBeNull();
|
||||
expect(messages).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('does not delete active workspaces', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
|
||||
const spoonId = await authed(t, ownerId).mutation(
|
||||
api.spoons.createManual,
|
||||
spoonInput,
|
||||
);
|
||||
const jobId = await createAgentJob(t, {
|
||||
ownerId,
|
||||
spoonId,
|
||||
status: 'running',
|
||||
workspaceStatus: 'active',
|
||||
});
|
||||
|
||||
await expect(
|
||||
authed(t, ownerId).mutation(api.agentJobs.deleteWorkspace, { jobId }),
|
||||
).rejects.toThrow('Only stopped, cancelled, failed, or expired workspaces');
|
||||
});
|
||||
|
||||
test('does not delete another user’s workspace', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
|
||||
const otherId = (await createUser(t, 'other@example.com')) as Id<'users'>;
|
||||
const spoonId = await authed(t, ownerId).mutation(
|
||||
api.spoons.createManual,
|
||||
spoonInput,
|
||||
);
|
||||
const jobId = await createAgentJob(t, {
|
||||
ownerId,
|
||||
spoonId,
|
||||
status: 'cancelled',
|
||||
workspaceStatus: 'stopped',
|
||||
});
|
||||
|
||||
await expect(
|
||||
authed(t, otherId).mutation(api.agentJobs.deleteWorkspace, { jobId }),
|
||||
).rejects.toThrow('Agent job not found.');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user