Skip to content

Commit 4761dd2

Browse files
committed
feat: storage tools tests
1 parent 6ff6063 commit 4761dd2

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

packages/mcp-server-supabase/src/server.test.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,116 @@ describe('tools', () => {
540540
expect(result).toEqual('dummy-anon-key');
541541
});
542542

543+
test('list storage buckets', async () => {
544+
const { callTool } = await setup();
545+
546+
const org = await createOrganization({
547+
name: 'My Org',
548+
plan: 'free',
549+
allowed_release_channels: ['ga'],
550+
});
551+
552+
const project = await createProject({
553+
name: 'Project 1',
554+
region: 'us-east-1',
555+
organization_id: org.id,
556+
});
557+
project.status = 'ACTIVE_HEALTHY';
558+
559+
project.createStorageBucket('bucket1', true);
560+
project.createStorageBucket('bucket2', false);
561+
562+
const result = await callTool({
563+
name: 'list_storage_buckets',
564+
arguments: {
565+
project_id: project.id,
566+
},
567+
});
568+
569+
expect(Array.isArray(result)).toBe(true);
570+
expect(result.length).toBe(2);
571+
expect(result[0]).toEqual(expect.objectContaining({
572+
name: 'bucket1',
573+
public: true,
574+
created_at: expect.any(String),
575+
updated_at: expect.any(String),
576+
}));
577+
expect(result[1]).toEqual(expect.objectContaining({
578+
name: 'bucket2',
579+
public: false,
580+
created_at: expect.any(String),
581+
updated_at: expect.any(String),
582+
}));
583+
});
584+
585+
test('get storage config', async () => {
586+
const { callTool } = await setup();
587+
588+
const org = await createOrganization({
589+
name: 'My Org',
590+
plan: 'free',
591+
allowed_release_channels: ['ga'],
592+
});
593+
594+
const project = await createProject({
595+
name: 'Project 1',
596+
region: 'us-east-1',
597+
organization_id: org.id,
598+
});
599+
project.status = 'ACTIVE_HEALTHY';
600+
601+
const result = await callTool({
602+
name: 'get_storage_config',
603+
arguments: {
604+
project_id: project.id,
605+
},
606+
});
607+
608+
expect(result).toEqual({
609+
fileSizeLimit: expect.any(Number),
610+
features: {
611+
imageTransformation: { enabled: expect.any(Boolean) },
612+
s3Protocol: { enabled: expect.any(Boolean) },
613+
},
614+
});
615+
});
616+
617+
test('update storage config', async () => {
618+
const { callTool } = await setup();
619+
620+
const org = await createOrganization({
621+
name: 'My Org',
622+
plan: 'free',
623+
allowed_release_channels: ['ga'],
624+
});
625+
626+
const project = await createProject({
627+
name: 'Project 1',
628+
region: 'us-east-1',
629+
organization_id: org.id,
630+
});
631+
project.status = 'ACTIVE_HEALTHY';
632+
633+
const config = {
634+
fileSizeLimit: 50,
635+
features: {
636+
imageTransformation: { enabled: true },
637+
s3Protocol: { enabled: false },
638+
},
639+
};
640+
641+
const result = await callTool({
642+
name: 'update_storage_config',
643+
arguments: {
644+
project_id: project.id,
645+
config,
646+
},
647+
});
648+
649+
// Update should succeed without error
650+
expect(result).toBeUndefined();
651+
});
652+
543653
test('execute sql', async () => {
544654
const { callTool } = await setup();
545655

packages/mcp-server-supabase/test/mocks.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,83 @@ export const mockManagementApi = [
672672
return HttpResponse.json({ message: 'ok' });
673673
}
674674
),
675+
676+
/**
677+
* List storage buckets
678+
*/
679+
http.get<{ ref: string }>(
680+
`${API_URL}/v1/projects/:ref/storage/buckets`,
681+
({ params }) => {
682+
const project = mockProjects.get(params.ref);
683+
if (!project) {
684+
return HttpResponse.json(
685+
{ message: 'Project not found' },
686+
{ status: 404 }
687+
);
688+
}
689+
690+
const buckets = Array.from(project.storage_buckets.values()).map(bucket => ({
691+
id: bucket.id,
692+
name: bucket.name,
693+
public: bucket.public,
694+
created_at: bucket.created_at.toISOString(),
695+
updated_at: bucket.updated_at.toISOString(),
696+
}));
697+
698+
return HttpResponse.json(buckets);
699+
}
700+
),
701+
702+
/**
703+
* Get storage config
704+
*/
705+
http.get<{ ref: string }>(
706+
`${API_URL}/v1/projects/:ref/config/storage`,
707+
({ params }) => {
708+
const project = mockProjects.get(params.ref);
709+
if (!project) {
710+
return HttpResponse.json(
711+
{ message: 'Project not found' },
712+
{ status: 404 }
713+
);
714+
}
715+
716+
return HttpResponse.json({
717+
fileSizeLimit: 50,
718+
features: {
719+
imageTransformation: { enabled: true },
720+
s3Protocol: { enabled: false },
721+
}
722+
});
723+
}
724+
),
725+
726+
/**
727+
* Update storage config
728+
*/
729+
http.patch<{ ref: string }>(
730+
`${API_URL}/v1/projects/:ref/config/storage`,
731+
async ({ params, request }) => {
732+
const project = mockProjects.get(params.ref);
733+
if (!project) {
734+
return HttpResponse.json(
735+
{ message: 'Project not found' },
736+
{ status: 404 }
737+
);
738+
}
739+
740+
// Accept any valid config
741+
try {
742+
await request.json();
743+
return new HttpResponse(null, { status: 204 });
744+
} catch (e) {
745+
return HttpResponse.json(
746+
{ message: 'Invalid request body' },
747+
{ status: 400 }
748+
);
749+
}
750+
}
751+
)
675752
];
676753

677754
export async function createOrganization(options: MockOrganizationOptions) {
@@ -849,6 +926,27 @@ export class MockEdgeFunction {
849926
}
850927
}
851928

929+
export type MockStorageBucketOptions = {
930+
name: string;
931+
isPublic: boolean;
932+
};
933+
934+
export class MockStorageBucket {
935+
id: string;
936+
name: string;
937+
public: boolean;
938+
created_at: Date;
939+
updated_at: Date;
940+
941+
constructor({ name, isPublic }: MockStorageBucketOptions) {
942+
this.id = crypto.randomUUID();
943+
this.name = name;
944+
this.public = isPublic;
945+
this.created_at = new Date();
946+
this.updated_at = new Date();
947+
}
948+
}
949+
852950
export type MockProjectOptions = {
853951
name: string;
854952
region: string;
@@ -871,6 +969,7 @@ export class MockProject {
871969

872970
migrations: Migration[] = [];
873971
edge_functions = new Map<string, MockEdgeFunction>();
972+
storage_buckets = new Map<string, MockStorageBucket>();
874973

875974
#db?: PGliteInterface;
876975

@@ -958,6 +1057,20 @@ export class MockProject {
9581057
await this.#db.close();
9591058
}
9601059
}
1060+
1061+
createStorageBucket(name: string, isPublic: boolean = false): MockStorageBucket {
1062+
const id = nanoid();
1063+
const bucket: MockStorageBucket = {
1064+
id,
1065+
name,
1066+
public: isPublic,
1067+
created_at: new Date(),
1068+
updated_at: new Date(),
1069+
};
1070+
1071+
this.storage_buckets.set(id, bucket);
1072+
return bucket;
1073+
}
9611074
}
9621075

9631076
export type MockBranchOptions = {

0 commit comments

Comments
 (0)