Something went wrong. Try again.
A fork of @slices.network/slices forked from slices.network/slices
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301import { assertEquals, assertStringIncludes } from "jsr:@std/assert";import { Project } from "ts-morph";import { generateClient } from "../src/client.ts";import type { Lexicon } from "../src/mod.ts";
function createTestProject() { return new Project({ useInMemoryFileSystem: true });}
Deno.test("generateClient - creates nested client structure", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "com.example.post", definitions: { main: { type: "record", record: { type: "record", properties: { text: { type: "string" }, }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should create main AtProtoClient class assertStringIncludes(result, "export class AtProtoClient extends SlicesClient");
// Should create nested class structure assertStringIncludes(result, "class ComClient"); assertStringIncludes(result, "class PostExampleComClient");
// Should have nested properties assertStringIncludes(result, "readonly com: ComClient;"); assertStringIncludes(result, "readonly post: PostExampleComClient;");
// Should have OAuth client property assertStringIncludes(result, "readonly oauth?: OAuthClient | AuthProvider;");});
Deno.test("generateClient - creates CRUD methods for records", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "app.bsky.feed.post", definitions: { main: { type: "record", record: { type: "record", properties: { text: { type: "string" }, createdAt: { type: "string" }, }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should create CRUD methods assertStringIncludes(result, "async getRecords("); assertStringIncludes(result, "async getRecord("); assertStringIncludes(result, "async createRecord("); assertStringIncludes(result, "async updateRecord("); assertStringIncludes(result, "async deleteRecord("); assertStringIncludes(result, "async countRecords(");
// Should have proper return types assertStringIncludes(result, "Promise<GetRecordsResponse<AppBskyFeedPost>>"); assertStringIncludes(result, "Promise<RecordResponse<AppBskyFeedPost>>"); assertStringIncludes(result, 'return await this.client.getRecords(\'app.bsky.feed.post\', params);');});
Deno.test("generateClient - creates constructors with proper inheritance", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "com.example.test", definitions: { main: { type: "record", record: { type: "record", properties: { name: { type: "string" } }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Main client constructor assertStringIncludes(result, "constructor(baseUrl: string, sliceUri: string, oauthClient?: OAuthClient | AuthProvider)"); assertStringIncludes(result, "super(baseUrl, sliceUri, oauthClient);");
// Nested class constructors assertStringIncludes(result, "constructor(client: SlicesClient)"); assertStringIncludes(result, "this.client = client;");});
Deno.test("generateClient - creates client for network.slices lexicons", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "network.slices.slice", definitions: { main: { type: "record", record: { type: "record", properties: { name: { type: "string" } }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should create proper client structure for network.slices assertStringIncludes(result, "export class AtProtoClient extends SlicesClient"); assertStringIncludes(result, "class NetworkClient"); assertStringIncludes(result, "class SlicesNetworkClient"); assertStringIncludes(result, "readonly network: NetworkClient;"); assertStringIncludes(result, "readonly slices: SlicesNetworkClient;");
// Should include standard CRUD methods assertStringIncludes(result, "async getRecords("); assertStringIncludes(result, "async createRecord(");});
Deno.test("generateClient - sanitizes hyphenated NSIDs", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "network.slices.cyber-meteor-1637.actor.profile", definitions: { main: { type: "record", record: { type: "record", properties: { displayName: { type: "string" }, description: { type: "string" }, }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should sanitize hyphenated names to camelCase assertStringIncludes(result, "readonly cyberMeteor1637: CyberMeteor1637SlicesNetworkClient;"); assertStringIncludes(result, "class CyberMeteor1637SlicesNetworkClient"); assertStringIncludes(result, "class ActorCyberMeteor1637SlicesNetworkClient"); assertStringIncludes(result, "class ProfileActorCyberMeteor1637SlicesNetworkClient");
// Constructor should use sanitized property names assertStringIncludes(result, "this.cyberMeteor1637 = new CyberMeteor1637SlicesNetworkClient(");
// But should preserve original NSID in API calls assertStringIncludes(result, "'network.slices.cyber-meteor-1637.actor.profile'");});
Deno.test("generateClient - handles mixed lexicon types", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "app.bsky.feed.post", definitions: { main: { type: "record", record: { type: "record", properties: { text: { type: "string" } }, }, }, }, }, { id: "network.slices.slice", definitions: { main: { type: "record", record: { type: "record", properties: { name: { type: "string" } }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should include both app.bsky and network.slices clients assertStringIncludes(result, "class AppClient"); assertStringIncludes(result, "class NetworkClient"); assertStringIncludes(result, "readonly app: AppClient;"); assertStringIncludes(result, "readonly network: NetworkClient;");});
Deno.test("generateClient - handles deep nesting correctly", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "app.bsky.feed.post", definitions: { main: { type: "record", record: { type: "record", properties: { text: { type: "string" } }, }, }, }, }, { id: "app.bsky.actor.profile", definitions: { main: { type: "record", record: { type: "record", properties: { displayName: { type: "string" } }, }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should create proper nesting: app.bsky.feed and app.bsky.actor assertStringIncludes(result, "class AppClient"); assertStringIncludes(result, "class BskyAppClient"); assertStringIncludes(result, "class FeedBskyAppClient"); assertStringIncludes(result, "class ActorBskyAppClient");
// Should have proper property chains assertStringIncludes(result, "readonly app: AppClient;"); assertStringIncludes(result, "readonly bsky: BskyAppClient;"); assertStringIncludes(result, "readonly feed: FeedBskyAppClient;"); assertStringIncludes(result, "readonly actor: ActorBskyAppClient;");});
Deno.test("generateClient - creates no client when no record lexicons", () => { const project = createTestProject(); const sourceFile = project.createSourceFile("test.ts", "");
const lexicons: Lexicon[] = [ { id: "com.example.defs", definitions: { main: { type: "object", properties: { value: { type: "string" } }, }, }, }, ];
generateClient(sourceFile, lexicons); const result = sourceFile.getFullText();
// Should not create any client classes for non-record lexicons assertEquals(result.includes("export class AtProtoClient"), false); assertEquals(result.includes("class"), false);});