@syncular/server-dialect-sqlite 0.0.1-100

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.
@@ -0,0 +1,149 @@
1
+ import Database from 'bun:sqlite';
2
+ import { afterEach, describe, expect, it } from 'bun:test';
3
+ import type { SyncCoreDb } from '@syncular/server/schema';
4
+ import type { Dialect, QueryResult } from 'kysely';
5
+ import {
6
+ Kysely,
7
+ SqliteAdapter,
8
+ SqliteIntrospector,
9
+ SqliteQueryCompiler,
10
+ sql,
11
+ } from 'kysely';
12
+ import { createSqliteServerDialect } from './index';
13
+
14
+ describe('SqliteServerSyncDialect.ensureConsoleSchema', () => {
15
+ let db: Kysely<SyncCoreDb>;
16
+
17
+ function createTestDb(): Kysely<SyncCoreDb> {
18
+ const sqlite = new Database(':memory:');
19
+ const dialect: Dialect = {
20
+ createAdapter: () => new SqliteAdapter(),
21
+ createDriver: () => ({
22
+ init: async () => {},
23
+ acquireConnection: async () => ({
24
+ executeQuery: async <R>(compiledQuery: {
25
+ sql: string;
26
+ parameters: readonly unknown[];
27
+ }): Promise<QueryResult<R>> => {
28
+ const normalizedSql = compiledQuery.sql.trimStart().toLowerCase();
29
+ if (
30
+ normalizedSql.startsWith('select') ||
31
+ normalizedSql.startsWith('with') ||
32
+ normalizedSql.startsWith('pragma')
33
+ ) {
34
+ const rows = sqlite
35
+ .prepare(compiledQuery.sql)
36
+ .all(...(compiledQuery.parameters ?? [])) as R[];
37
+ return { rows };
38
+ }
39
+ const result = sqlite
40
+ .prepare(compiledQuery.sql)
41
+ .run(...(compiledQuery.parameters ?? []));
42
+ return {
43
+ rows: [] as R[],
44
+ numAffectedRows: BigInt(result.changes),
45
+ insertId:
46
+ result.lastInsertRowid != null
47
+ ? BigInt(result.lastInsertRowid)
48
+ : undefined,
49
+ };
50
+ },
51
+ streamQuery: <R>(): AsyncIterableIterator<QueryResult<R>> => {
52
+ throw new Error('Not implemented in test driver');
53
+ },
54
+ }),
55
+ beginTransaction: async () => {},
56
+ commitTransaction: async () => {},
57
+ rollbackTransaction: async () => {},
58
+ releaseConnection: async () => {},
59
+ destroy: async () => {
60
+ sqlite.close();
61
+ },
62
+ }),
63
+ createIntrospector: (innerDb) => new SqliteIntrospector(innerDb),
64
+ createQueryCompiler: () => new SqliteQueryCompiler(),
65
+ };
66
+
67
+ return new Kysely<SyncCoreDb>({ dialect });
68
+ }
69
+
70
+ afterEach(async () => {
71
+ if (db) {
72
+ await db.destroy();
73
+ }
74
+ });
75
+
76
+ it('creates sync_request_events with transport_path on new databases', async () => {
77
+ db = createTestDb();
78
+ const dialect = createSqliteServerDialect();
79
+
80
+ await dialect.ensureConsoleSchema(db);
81
+
82
+ const columns = await sql<{ name: string }>`
83
+ PRAGMA table_info(sync_request_events)
84
+ `.execute(db);
85
+
86
+ expect(columns.rows.map((row) => row.name)).toContain('transport_path');
87
+ });
88
+
89
+ it('adds transport_path when upgrading an existing sync_request_events table', async () => {
90
+ db = createTestDb();
91
+ const dialect = createSqliteServerDialect();
92
+
93
+ await db.schema
94
+ .createTable('sync_request_events')
95
+ .addColumn('event_id', 'integer', (col) => col.primaryKey())
96
+ .addColumn('event_type', 'text', (col) => col.notNull())
97
+ .addColumn('actor_id', 'text', (col) => col.notNull())
98
+ .addColumn('client_id', 'text', (col) => col.notNull())
99
+ .addColumn('status_code', 'integer', (col) => col.notNull())
100
+ .addColumn('outcome', 'text', (col) => col.notNull())
101
+ .addColumn('duration_ms', 'integer', (col) => col.notNull())
102
+ .addColumn('commit_seq', 'integer')
103
+ .addColumn('operation_count', 'integer')
104
+ .addColumn('row_count', 'integer')
105
+ .addColumn('tables', 'text', (col) => col.notNull().defaultTo('[]'))
106
+ .addColumn('error_message', 'text')
107
+ .addColumn('created_at', 'text', (col) => col.notNull())
108
+ .execute();
109
+
110
+ await dialect.ensureConsoleSchema(db);
111
+
112
+ const columns = await sql<{ name: string }>`
113
+ PRAGMA table_info(sync_request_events)
114
+ `.execute(db);
115
+ expect(columns.rows.map((row) => row.name)).toContain('transport_path');
116
+
117
+ await sql`
118
+ INSERT INTO sync_request_events (
119
+ event_type,
120
+ actor_id,
121
+ client_id,
122
+ status_code,
123
+ outcome,
124
+ duration_ms,
125
+ tables,
126
+ transport_path,
127
+ created_at
128
+ ) VALUES (
129
+ ${'pull'},
130
+ ${'actor-1'},
131
+ ${'client-1'},
132
+ ${200},
133
+ ${'ok'},
134
+ ${12},
135
+ ${'[]'},
136
+ ${'direct'},
137
+ ${'2026-02-12T00:00:00.000Z'}
138
+ )
139
+ `.execute(db);
140
+
141
+ const inserted = await sql<{ count: number }>`
142
+ SELECT COUNT(*) as count
143
+ FROM sync_request_events
144
+ WHERE transport_path = ${'direct'}
145
+ `.execute(db);
146
+
147
+ expect(inserted.rows[0]?.count ?? 0).toBe(1);
148
+ });
149
+ });