@livestore/sqlite-wasm 0.4.0-dev.1 → 0.4.0-dev.10
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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser/mod.d.ts +1 -0
- package/dist/browser/mod.d.ts.map +1 -1
- package/dist/browser/mod.js.map +1 -1
- package/dist/browser/opfs/AccessHandlePoolVFS.d.ts +17 -0
- package/dist/browser/opfs/AccessHandlePoolVFS.d.ts.map +1 -1
- package/dist/browser/opfs/AccessHandlePoolVFS.js +72 -1
- package/dist/browser/opfs/AccessHandlePoolVFS.js.map +1 -1
- package/dist/cf/BlockManager.d.ts +61 -0
- package/dist/cf/BlockManager.d.ts.map +1 -0
- package/dist/cf/BlockManager.js +157 -0
- package/dist/cf/BlockManager.js.map +1 -0
- package/dist/cf/CloudflareSqlVFS.d.ts +51 -0
- package/dist/cf/CloudflareSqlVFS.d.ts.map +1 -0
- package/dist/cf/CloudflareSqlVFS.js +351 -0
- package/dist/cf/CloudflareSqlVFS.js.map +1 -0
- package/dist/cf/CloudflareWorkerVFS.d.ts +72 -0
- package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -0
- package/dist/cf/CloudflareWorkerVFS.js +552 -0
- package/dist/cf/CloudflareWorkerVFS.js.map +1 -0
- package/dist/cf/mod.d.ts +43 -0
- package/dist/cf/mod.d.ts.map +1 -0
- package/dist/cf/mod.js +74 -0
- package/dist/cf/mod.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +314 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +266 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +462 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +334 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts +2 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts.map +1 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +354 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -0
- package/dist/load-wasm/mod.node.d.ts.map +1 -1
- package/dist/load-wasm/mod.node.js +1 -2
- package/dist/load-wasm/mod.node.js.map +1 -1
- package/dist/load-wasm/mod.workerd.d.ts +2 -0
- package/dist/load-wasm/mod.workerd.d.ts.map +1 -0
- package/dist/load-wasm/mod.workerd.js +26 -0
- package/dist/load-wasm/mod.workerd.js.map +1 -0
- package/dist/make-sqlite-db.d.ts +1 -0
- package/dist/make-sqlite-db.d.ts.map +1 -1
- package/dist/make-sqlite-db.js +28 -4
- package/dist/make-sqlite-db.js.map +1 -1
- package/dist/node/NodeFS.d.ts +1 -2
- package/dist/node/NodeFS.d.ts.map +1 -1
- package/dist/node/NodeFS.js +1 -6
- package/dist/node/NodeFS.js.map +1 -1
- package/dist/node/mod.js +3 -8
- package/dist/node/mod.js.map +1 -1
- package/package.json +21 -8
- package/src/browser/mod.ts +1 -0
- package/src/browser/opfs/AccessHandlePoolVFS.ts +79 -1
- package/src/cf/BlockManager.ts +225 -0
- package/src/cf/CloudflareSqlVFS.ts +450 -0
- package/src/cf/CloudflareWorkerVFS.ts +664 -0
- package/src/cf/README.md +60 -0
- package/src/cf/mod.ts +143 -0
- package/src/cf/test/README.md +224 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +389 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +322 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +585 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +403 -0
- package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +433 -0
- package/src/load-wasm/mod.node.ts +1 -2
- package/src/load-wasm/mod.workerd.ts +26 -0
- package/src/make-sqlite-db.ts +38 -4
- package/src/node/NodeFS.ts +1 -9
- package/src/node/mod.ts +3 -10
package/src/cf/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# CF SQLite WASM
|
|
2
|
+
|
|
3
|
+
## VFS SQL Schema for Cloudflare Durable Object SQL Storage
|
|
4
|
+
|
|
5
|
+
This schema implements a block-based file storage system on top of SQLite
|
|
6
|
+
|
|
7
|
+
## Schema
|
|
8
|
+
|
|
9
|
+
```sql
|
|
10
|
+
|
|
11
|
+
-- File metadata table
|
|
12
|
+
-- Stores information about each virtual file in the VFS
|
|
13
|
+
CREATE TABLE IF NOT EXISTS vfs_files (
|
|
14
|
+
file_path TEXT PRIMARY KEY,
|
|
15
|
+
file_size INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
flags INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
18
|
+
modified_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- Block-based file data storage
|
|
22
|
+
-- Files are split into fixed-size blocks for efficient I/O operations
|
|
23
|
+
CREATE TABLE IF NOT EXISTS vfs_blocks (
|
|
24
|
+
file_path TEXT NOT NULL,
|
|
25
|
+
block_id INTEGER NOT NULL,
|
|
26
|
+
block_data BLOB NOT NULL,
|
|
27
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
28
|
+
PRIMARY KEY (file_path, block_id),
|
|
29
|
+
FOREIGN KEY (file_path) REFERENCES vfs_files(file_path) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
-- Index for efficient block range queries
|
|
33
|
+
-- Essential for performance when reading/writing sequential blocks
|
|
34
|
+
CREATE INDEX IF NOT EXISTS idx_vfs_blocks_range ON vfs_blocks(file_path, block_id);
|
|
35
|
+
|
|
36
|
+
-- Index for file metadata queries
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_vfs_files_modified ON vfs_files(modified_at);
|
|
38
|
+
|
|
39
|
+
-- Trigger to update modified_at timestamp when file size changes
|
|
40
|
+
CREATE TRIGGER IF NOT EXISTS trg_vfs_files_update_modified
|
|
41
|
+
AFTER UPDATE OF file_size ON vfs_files
|
|
42
|
+
BEGIN
|
|
43
|
+
UPDATE vfs_files SET modified_at = unixepoch() WHERE file_path = NEW.file_path;
|
|
44
|
+
END;
|
|
45
|
+
|
|
46
|
+
-- View for file statistics (useful for debugging and monitoring)
|
|
47
|
+
CREATE VIEW IF NOT EXISTS vfs_file_stats AS
|
|
48
|
+
SELECT
|
|
49
|
+
f.file_path,
|
|
50
|
+
f.file_size,
|
|
51
|
+
f.flags,
|
|
52
|
+
f.created_at,
|
|
53
|
+
f.modified_at,
|
|
54
|
+
COUNT(b.block_id) as block_count,
|
|
55
|
+
COALESCE(SUM(LENGTH(b.block_data)), 0) as stored_bytes,
|
|
56
|
+
ROUND(CAST(COALESCE(SUM(LENGTH(b.block_data)), 0) AS REAL) / NULLIF(f.file_size, 0) * 100, 2) as compression_ratio
|
|
57
|
+
FROM vfs_files f
|
|
58
|
+
LEFT JOIN vfs_blocks b ON f.file_path = b.file_path
|
|
59
|
+
GROUP BY f.file_path, f.file_size, f.flags, f.created_at, f.modified_at;
|
|
60
|
+
```
|
package/src/cf/mod.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import { type MakeSqliteDb, type PersistenceInfo, type SqliteDb, UnexpectedError } from '@livestore/common'
|
|
4
|
+
import type { CfTypes } from '@livestore/common-cf'
|
|
5
|
+
import { Effect } from '@livestore/utils/effect'
|
|
6
|
+
import type * as WaSqlite from '@livestore/wa-sqlite'
|
|
7
|
+
import type { MemoryVFS } from '@livestore/wa-sqlite/src/examples/MemoryVFS.js'
|
|
8
|
+
import { makeInMemoryDb } from '../in-memory-vfs.ts'
|
|
9
|
+
import { makeSqliteDb } from '../make-sqlite-db.ts'
|
|
10
|
+
import { CloudflareSqlVFS } from './CloudflareSqlVFS.ts'
|
|
11
|
+
|
|
12
|
+
export { BlockManager } from './BlockManager.ts'
|
|
13
|
+
export { CloudflareSqlVFS } from './CloudflareSqlVFS.ts'
|
|
14
|
+
export { CloudflareWorkerVFS } from './CloudflareWorkerVFS.ts'
|
|
15
|
+
|
|
16
|
+
export type CloudflareDatabaseMetadataInMemory = {
|
|
17
|
+
_tag: 'in-memory'
|
|
18
|
+
vfs: MemoryVFS
|
|
19
|
+
dbPointer: number
|
|
20
|
+
persistenceInfo: PersistenceInfo
|
|
21
|
+
deleteDb: () => void
|
|
22
|
+
configureDb: (db: SqliteDb) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CloudflareDatabaseMetadataFs = {
|
|
26
|
+
_tag: 'storage'
|
|
27
|
+
// vfs: CloudflareWorkerVFS
|
|
28
|
+
vfs: CloudflareSqlVFS
|
|
29
|
+
dbPointer: number
|
|
30
|
+
persistenceInfo: PersistenceInfo
|
|
31
|
+
deleteDb: () => void
|
|
32
|
+
configureDb: (db: SqliteDb) => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type CloudflareDatabaseMetadata = CloudflareDatabaseMetadataInMemory | CloudflareDatabaseMetadataFs
|
|
36
|
+
|
|
37
|
+
export type CloudflareDatabaseInputInMemory = {
|
|
38
|
+
_tag: 'in-memory'
|
|
39
|
+
configureDb?: (db: SqliteDb) => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type CloudflareDatabaseInputFs = {
|
|
43
|
+
_tag: 'storage'
|
|
44
|
+
// directory: string
|
|
45
|
+
fileName: string
|
|
46
|
+
configureDb?: (db: SqliteDb) => void
|
|
47
|
+
storage: CfTypes.DurableObjectStorage
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type CloudflareDatabaseInput = CloudflareDatabaseInputInMemory | CloudflareDatabaseInputFs
|
|
51
|
+
|
|
52
|
+
export type MakeCloudflareSqliteDb = MakeSqliteDb<
|
|
53
|
+
{ dbPointer: number; persistenceInfo: PersistenceInfo },
|
|
54
|
+
CloudflareDatabaseInput,
|
|
55
|
+
CloudflareDatabaseMetadata
|
|
56
|
+
>
|
|
57
|
+
|
|
58
|
+
export const sqliteDbFactory =
|
|
59
|
+
({ sqlite3 }: { sqlite3: SQLiteAPI }): MakeCloudflareSqliteDb =>
|
|
60
|
+
(input) =>
|
|
61
|
+
Effect.gen(function* () {
|
|
62
|
+
if (input._tag === 'in-memory') {
|
|
63
|
+
const { dbPointer, vfs } = makeInMemoryDb(sqlite3)
|
|
64
|
+
return makeSqliteDb<CloudflareDatabaseMetadataInMemory>({
|
|
65
|
+
sqlite3,
|
|
66
|
+
metadata: {
|
|
67
|
+
_tag: 'in-memory',
|
|
68
|
+
vfs,
|
|
69
|
+
dbPointer,
|
|
70
|
+
persistenceInfo: { fileName: ':memory:' },
|
|
71
|
+
deleteDb: () => {},
|
|
72
|
+
configureDb: input.configureDb ?? (() => {}),
|
|
73
|
+
},
|
|
74
|
+
}) as any
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { dbPointer, vfs } = yield* makeCloudflareFsDb({
|
|
78
|
+
sqlite3,
|
|
79
|
+
fileName: input.fileName,
|
|
80
|
+
// directory: input.directory,
|
|
81
|
+
storage: input.storage,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// const filePath = path.join(input.directory, input.fileName)
|
|
85
|
+
// const filePath = `${input.directory}/${input.fileName}`
|
|
86
|
+
|
|
87
|
+
return makeSqliteDb<CloudflareDatabaseMetadataFs>({
|
|
88
|
+
sqlite3,
|
|
89
|
+
metadata: {
|
|
90
|
+
_tag: 'storage',
|
|
91
|
+
vfs,
|
|
92
|
+
dbPointer,
|
|
93
|
+
persistenceInfo: { fileName: input.fileName },
|
|
94
|
+
// deleteDb: () => vfs.deleteDb(filePath),
|
|
95
|
+
// TODO: implement deleteDb
|
|
96
|
+
deleteDb: () => {},
|
|
97
|
+
configureDb: input.configureDb ?? (() => {}),
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const makeCloudflareFsDb = ({
|
|
103
|
+
sqlite3,
|
|
104
|
+
fileName,
|
|
105
|
+
// directory,
|
|
106
|
+
storage,
|
|
107
|
+
}: {
|
|
108
|
+
sqlite3: WaSqlite.SQLiteAPI
|
|
109
|
+
fileName: string
|
|
110
|
+
// directory: string
|
|
111
|
+
storage: CfTypes.DurableObjectStorage
|
|
112
|
+
}) =>
|
|
113
|
+
Effect.gen(function* () {
|
|
114
|
+
// NOTE to keep the filePath short, we use the directory name in the vfs name
|
|
115
|
+
// If this is becoming a problem, we can use a hashed version of the directory name
|
|
116
|
+
const vfsName = `cf-do-sqlite-${fileName}`
|
|
117
|
+
if (sqlite3.vfs_registered.has(vfsName) === false) {
|
|
118
|
+
// TODO refactor with Effect FileSystem instead of using `node:fs` directly inside of CloudflareWorkerVFS
|
|
119
|
+
// const nodeFsVfs = new CloudflareWorkerVFS(vfsName, storage, (sqlite3 as any).module)
|
|
120
|
+
const nodeFsVfs = new CloudflareSqlVFS(vfsName, storage.sql, (sqlite3 as any).module)
|
|
121
|
+
|
|
122
|
+
// Initialize the VFS schema before registering it
|
|
123
|
+
const isReady = yield* Effect.promise(() => nodeFsVfs.isReady())
|
|
124
|
+
if (!isReady) {
|
|
125
|
+
throw new Error(`Failed to initialize CloudflareSqlVFS for ${vfsName}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// @ts-expect-error TODO fix types
|
|
129
|
+
sqlite3.vfs_register(nodeFsVfs, false)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// yield* fs.makeDirectory(directory, { recursive: true })
|
|
133
|
+
|
|
134
|
+
const FILE_NAME_MAX_LENGTH = 56
|
|
135
|
+
if (fileName.length > FILE_NAME_MAX_LENGTH) {
|
|
136
|
+
throw new Error(`File name ${fileName} is too long. Maximum length is ${FILE_NAME_MAX_LENGTH} characters.`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// NOTE SQLite will return a "disk I/O error" if the file path is too long.
|
|
140
|
+
const dbPointer = sqlite3.open_v2Sync(fileName, undefined, vfsName)
|
|
141
|
+
|
|
142
|
+
return { dbPointer, vfs: {} as UNUSED<'only needed in web adapter currently and should longer-term be removed'> }
|
|
143
|
+
}).pipe(UnexpectedError.mapToUnexpectedError)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Cloudflare SQLite WASM Test Suite
|
|
2
|
+
|
|
3
|
+
This directory contains comprehensive tests for both SQLite WASM VFS implementations on Cloudflare Workers.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
### `/sql/` - SQL Storage Tests
|
|
8
|
+
Tests for the CloudflareSqlVFS implementation, which uses Cloudflare's DurableObject SQL API for storage.
|
|
9
|
+
|
|
10
|
+
- **`cloudflare-sql-vfs-core.test.ts`** - Core SQL VFS functionality tests
|
|
11
|
+
|
|
12
|
+
### `/async-storage/` - Async Storage Tests
|
|
13
|
+
Tests for the CloudflareWorkerVFS implementation, which uses DurableObjectStorage (key-value) for file storage.
|
|
14
|
+
|
|
15
|
+
- **`cloudflare-worker-vfs-core.test.ts`** - Basic VFS operations (open, read, write, close, sync)
|
|
16
|
+
- **`cloudflare-worker-vfs-advanced.test.ts`** - Large file chunking and advanced features
|
|
17
|
+
- **`cloudflare-worker-vfs-reliability.test.ts`** - Error recovery and reliability testing
|
|
18
|
+
- **`cloudflare-worker-vfs-integration.test.ts`** - End-to-end integration tests
|
|
19
|
+
|
|
20
|
+
## Test Architecture
|
|
21
|
+
|
|
22
|
+
### Testing Framework
|
|
23
|
+
- **Vitest** with **@cloudflare/vitest-pool-workers** for Workers runtime testing
|
|
24
|
+
- **Isolated Storage** - Each test gets fresh storage instances
|
|
25
|
+
- **Real Runtime** - Tests run in the actual Cloudflare Workers runtime environment
|
|
26
|
+
|
|
27
|
+
## VFS Implementation Comparison
|
|
28
|
+
|
|
29
|
+
### SQL Storage VFS (`CloudflareSqlVFS`)
|
|
30
|
+
- **Backend**: Cloudflare DurableObject SQL API
|
|
31
|
+
- **Storage Model**: Relational tables with blocks and metadata
|
|
32
|
+
- **Advantages**: ACID transactions, complex queries, relational integrity
|
|
33
|
+
- **Use Case**: Applications requiring complex data relationships
|
|
34
|
+
|
|
35
|
+
### Async Storage VFS (`CloudflareWorkerVFS`)
|
|
36
|
+
- **Backend**: DurableObjectStorage (key-value)
|
|
37
|
+
- **Storage Model**: 64 KiB chunks with LRU caching
|
|
38
|
+
- **Advantages**: Simple API, optimized for large files, better caching
|
|
39
|
+
- **Use Case**: High-performance file operations, large databases
|
|
40
|
+
|
|
41
|
+
## Key Design Decisions Tested
|
|
42
|
+
|
|
43
|
+
### SQL Storage Approach
|
|
44
|
+
- **Block-based Storage**: Files stored as blocks in SQL tables
|
|
45
|
+
- **Metadata Management**: File metadata in dedicated tables
|
|
46
|
+
- **Transaction Safety**: ACID compliance for data integrity
|
|
47
|
+
|
|
48
|
+
### Async Storage Approach
|
|
49
|
+
- **64 KiB Chunking Strategy**: Optimized for SQLite I/O patterns
|
|
50
|
+
- **Synchronous Interface with Async Backend**: Aggressive caching + background sync
|
|
51
|
+
- **Memory Management**: LRU cache for chunks, complete metadata cache
|
|
52
|
+
- **Error Handling**: SQLite-compatible error codes
|
|
53
|
+
|
|
54
|
+
## Running Tests
|
|
55
|
+
|
|
56
|
+
### Prerequisites
|
|
57
|
+
1. Install dependencies:
|
|
58
|
+
```bash
|
|
59
|
+
pnpm install
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. Ensure Wrangler configuration is correct:
|
|
63
|
+
```bash
|
|
64
|
+
# Check wrangler.toml exists and has correct Durable Object bindings
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Test Commands
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Run all tests
|
|
71
|
+
pnpm test
|
|
72
|
+
|
|
73
|
+
# Run tests in watch mode
|
|
74
|
+
pnpm test:watch
|
|
75
|
+
|
|
76
|
+
# Run tests with UI
|
|
77
|
+
pnpm test:ui
|
|
78
|
+
|
|
79
|
+
# Run specific test directory
|
|
80
|
+
pnpm test src/cf/ # All cf tests (SQL + async storage)
|
|
81
|
+
pnpm test src/cf/test/sql/ # SQL storage tests only
|
|
82
|
+
pnpm test src/cf/test/async-storage/ # Async storage tests only
|
|
83
|
+
|
|
84
|
+
# Run specific test file
|
|
85
|
+
pnpm test cloudflare-sql-vfs-core.test.ts
|
|
86
|
+
|
|
87
|
+
# Run with coverage
|
|
88
|
+
pnpm test --coverage
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Test Environment
|
|
92
|
+
|
|
93
|
+
- **Runtime**: Cloudflare Workers (via workerd)
|
|
94
|
+
- **Storage**: Isolated per-test storage instances
|
|
95
|
+
|
|
96
|
+
## Test Structure
|
|
97
|
+
|
|
98
|
+
### SQL Storage Test Pattern
|
|
99
|
+
```typescript
|
|
100
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
101
|
+
import { type Cf, CloudflareSqlVFS } from '../../mod.ts'
|
|
102
|
+
|
|
103
|
+
describe('SQL VFS Test Suite', () => {
|
|
104
|
+
let vfs: CloudflareSqlVFS
|
|
105
|
+
let mockSql: Cf.SqlStorage
|
|
106
|
+
let queryLog: string[]
|
|
107
|
+
|
|
108
|
+
beforeEach(async () => {
|
|
109
|
+
// Setup mock SQL storage
|
|
110
|
+
mockSql = {
|
|
111
|
+
exec: (query: string, ...bindings: any[]) => {
|
|
112
|
+
// Mock SQL implementation
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
vfs = new CloudflareSqlVFS('test-vfs', mockSql, {})
|
|
117
|
+
await vfs.isReady()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// Tests...
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Async Storage Test Pattern
|
|
125
|
+
```typescript
|
|
126
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
127
|
+
import { type Cf, CloudflareWorkerVFS } from '../../mod.ts'
|
|
128
|
+
|
|
129
|
+
describe('Async Storage VFS Test Suite', () => {
|
|
130
|
+
let vfs: CloudflareWorkerVFS
|
|
131
|
+
let mockStorage: DurableObjectStorage
|
|
132
|
+
|
|
133
|
+
beforeEach(async () => {
|
|
134
|
+
// Setup mock DurableObjectStorage
|
|
135
|
+
mockStorage = {
|
|
136
|
+
get: async (key) => { /* mock implementation */ },
|
|
137
|
+
put: async (key, value) => { /* mock implementation */ },
|
|
138
|
+
delete: async (key) => { /* mock implementation */ },
|
|
139
|
+
// ... other methods
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
vfs = new CloudflareWorkerVFS('test-vfs', mockStorage, {})
|
|
143
|
+
await vfs.isReady()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Tests...
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Performance Benchmarks
|
|
151
|
+
|
|
152
|
+
### Metrics Tracked
|
|
153
|
+
- **Throughput**: Operations per second
|
|
154
|
+
- **Latency**: Response times for various operations
|
|
155
|
+
- **Memory Usage**: Cache effectiveness and memory consumption
|
|
156
|
+
- **Storage Efficiency**: Data compression and chunking effectiveness
|
|
157
|
+
|
|
158
|
+
### Benchmark Comparisons
|
|
159
|
+
- SQL vs Async Storage performance characteristics
|
|
160
|
+
- Memory usage patterns between implementations
|
|
161
|
+
- Scalability under different workloads
|
|
162
|
+
|
|
163
|
+
## Test Data Cleanup
|
|
164
|
+
|
|
165
|
+
### Automatic Cleanup
|
|
166
|
+
- **Isolated Storage**: Each test gets fresh storage instances
|
|
167
|
+
- **Temporary Files**: Cleaned up automatically
|
|
168
|
+
- **Cache Management**: Memory caches cleared between tests
|
|
169
|
+
|
|
170
|
+
## Debugging Tests
|
|
171
|
+
|
|
172
|
+
### Logging
|
|
173
|
+
- Use `console.log` for debugging (visible in test output)
|
|
174
|
+
- VFS statistics available via `vfs.getStats()`
|
|
175
|
+
- Storage operations logged in development mode
|
|
176
|
+
|
|
177
|
+
### Common Issues
|
|
178
|
+
1. **SQL Schema Issues**: Ensure proper table creation and constraints
|
|
179
|
+
2. **Cache Misses**: Verify proper preloading in VFS initialization
|
|
180
|
+
3. **Async/Sync Mismatch**: Check async operations are properly handled
|
|
181
|
+
4. **Storage Limits**: Verify chunk sizes and storage capacity
|
|
182
|
+
|
|
183
|
+
### Test Debugging
|
|
184
|
+
```typescript
|
|
185
|
+
// SQL VFS debugging
|
|
186
|
+
console.log('Query log:', queryLog)
|
|
187
|
+
const stats = vfs.getStats()
|
|
188
|
+
console.log('VFS Stats:', stats)
|
|
189
|
+
|
|
190
|
+
// Async Storage VFS debugging
|
|
191
|
+
const metadata = await storage.get('file:/test/file.db:meta')
|
|
192
|
+
console.log('File metadata:', metadata)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Future Enhancements
|
|
196
|
+
|
|
197
|
+
### Planned Test Additions
|
|
198
|
+
1. **Performance Comparisons**: Direct SQL vs Async Storage benchmarks
|
|
199
|
+
2. **Migration Tests**: Converting between storage backends
|
|
200
|
+
3. **Stress Tests**: High-load scenarios for both implementations
|
|
201
|
+
4. **Real SQLite Integration**: Tests with actual SQLite WASM
|
|
202
|
+
|
|
203
|
+
### Implementation Improvements
|
|
204
|
+
1. **Hybrid Approach**: Combining SQL and async storage benefits
|
|
205
|
+
2. **Compression Testing**: Data compression effectiveness
|
|
206
|
+
3. **Caching Strategies**: Cross-implementation cache optimization
|
|
207
|
+
|
|
208
|
+
## Contributing
|
|
209
|
+
|
|
210
|
+
When adding new tests:
|
|
211
|
+
1. Choose the appropriate location:
|
|
212
|
+
- SQL storage tests: `/src/cf/test/sql/`
|
|
213
|
+
- Async storage tests: `/src/cf/test/async-storage/`
|
|
214
|
+
2. Follow the existing test structure for that implementation
|
|
215
|
+
3. Use descriptive test names indicating the VFS type
|
|
216
|
+
4. Include both positive and negative test cases
|
|
217
|
+
5. Update this documentation
|
|
218
|
+
|
|
219
|
+
### Test Guidelines
|
|
220
|
+
- **Isolation**: Each test should be independent
|
|
221
|
+
- **Implementation Specific**: Tests should target specific VFS features
|
|
222
|
+
- **Assertions**: Use meaningful assertions with clear error messages
|
|
223
|
+
- **Coverage**: Aim for comprehensive coverage of edge cases
|
|
224
|
+
- **Performance**: Consider performance implications of each approach
|