braid-text 0.2.107 → 0.2.109
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/index.js +7 -2
- package/package.json +12 -2
- package/.claude/settings.local.json +0 -9
- package/test/fuzz-test.js +0 -643
- package/test/test.html +0 -118
- package/test/test.js +0 -321
- package/test/tests.js +0 -2899
package/test/test.html
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
<style>
|
|
2
|
-
body {
|
|
3
|
-
font-family: Arial, sans-serif;
|
|
4
|
-
max-width: 800px;
|
|
5
|
-
margin: 0 auto;
|
|
6
|
-
padding: 10px;
|
|
7
|
-
}
|
|
8
|
-
.test {
|
|
9
|
-
margin-bottom: 3px;
|
|
10
|
-
padding: 3px;
|
|
11
|
-
}
|
|
12
|
-
.running {
|
|
13
|
-
background-color: #fffde7;
|
|
14
|
-
}
|
|
15
|
-
.passed {
|
|
16
|
-
background-color: #e8f5e9;
|
|
17
|
-
}
|
|
18
|
-
.failed {
|
|
19
|
-
background-color: #ffebee;
|
|
20
|
-
}
|
|
21
|
-
#summaryContainer {
|
|
22
|
-
display: flex;
|
|
23
|
-
flex-wrap: wrap;
|
|
24
|
-
gap: 5px;
|
|
25
|
-
margin-bottom: 20px;
|
|
26
|
-
}
|
|
27
|
-
.summaryBox {
|
|
28
|
-
width: 25px;
|
|
29
|
-
height: 25px;
|
|
30
|
-
border: 1px solid #ddd;
|
|
31
|
-
}
|
|
32
|
-
</style>
|
|
33
|
-
<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
|
|
34
|
-
<script src="./tests.js"></script>
|
|
35
|
-
<div id="summaryContainer"></div>
|
|
36
|
-
<div id="testContainer"></div>
|
|
37
|
-
<script type=module>
|
|
38
|
-
|
|
39
|
-
import {
|
|
40
|
-
default as init,
|
|
41
|
-
Doc,
|
|
42
|
-
OpLog,
|
|
43
|
-
} from "https://unpkg.com/diamond-types-web";
|
|
44
|
-
|
|
45
|
-
// Make diamond-types available globally for tests.js
|
|
46
|
-
window.Doc = Doc
|
|
47
|
-
window.OpLog = OpLog
|
|
48
|
-
window.dt_p = init()
|
|
49
|
-
|
|
50
|
-
braid_fetch.enable_multiplex = false
|
|
51
|
-
|
|
52
|
-
// Parse URL parameters for filter
|
|
53
|
-
const urlParams = new URLSearchParams(window.location.search)
|
|
54
|
-
const filterArg = urlParams.get('filter') || urlParams.get('grep')
|
|
55
|
-
|
|
56
|
-
let delay = 0
|
|
57
|
-
|
|
58
|
-
function createTestDiv(testName) {
|
|
59
|
-
const div = document.createElement("div")
|
|
60
|
-
div.className = "test running"
|
|
61
|
-
div.innerHTML = `<span style="font-weight:bold">${testName}: </span><span class="result">Running...</span>`
|
|
62
|
-
testContainer.appendChild(div)
|
|
63
|
-
return div
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function updateTestResult(div, passed, message, got, expected) {
|
|
67
|
-
div.className = `test ${passed ? "passed" : "failed"}`
|
|
68
|
-
|
|
69
|
-
if (passed) {
|
|
70
|
-
div.querySelector(".result").textContent = message
|
|
71
|
-
div.querySelector(".result").style.fontSize = message.length > 400 ? 'xx-small' : message.length > 100 ? 'small' : ''
|
|
72
|
-
} else {
|
|
73
|
-
div.querySelector(".result").innerHTML = `${message}<br><strong>Got:</strong> ${got}<br><strong>Expected:</strong> ${expected}`
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function createSummaryBox() {
|
|
78
|
-
var summaryContainer = document.getElementById('summaryContainer')
|
|
79
|
-
const box = document.createElement('div');
|
|
80
|
-
box.className = 'summaryBox running';
|
|
81
|
-
summaryContainer.appendChild(box);
|
|
82
|
-
return box;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function updateSummaryBox(box, passed) {
|
|
86
|
-
box.className = `summaryBox ${passed ? 'passed' : passed === false ? 'failed' : 'other'}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function runTest(testName, testFunction, expectedResult) {
|
|
90
|
-
// Apply filter if specified
|
|
91
|
-
if (filterArg && !testName.toLowerCase().includes(filterArg.toLowerCase())) {
|
|
92
|
-
return // Skip this test
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
delay += 70
|
|
96
|
-
|
|
97
|
-
await new Promise(done => setTimeout(done, delay))
|
|
98
|
-
const div = createTestDiv(testName)
|
|
99
|
-
const summaryBox = createSummaryBox()
|
|
100
|
-
try {
|
|
101
|
-
let x = await testFunction()
|
|
102
|
-
if (x == expectedResult) {
|
|
103
|
-
updateTestResult(div, true, x)
|
|
104
|
-
updateSummaryBox(summaryBox, true)
|
|
105
|
-
} else {
|
|
106
|
-
updateTestResult(div, false, "Mismatch:", x, expectedResult)
|
|
107
|
-
updateSummaryBox(summaryBox, false)
|
|
108
|
-
}
|
|
109
|
-
} catch (error) {
|
|
110
|
-
updateTestResult(div, false, "Error:", error.message || error, expectedResult)
|
|
111
|
-
updateSummaryBox(summaryBox, false)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Load and run all tests from the shared tests.js file
|
|
116
|
-
defineTests(runTest, braid_fetch)
|
|
117
|
-
|
|
118
|
-
</script>
|
package/test/test.js
DELETED
|
@@ -1,321 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// Unified test runner - can run in console mode (Node.js) or browser mode (server)
|
|
4
|
-
const http = require('http')
|
|
5
|
-
const {fetch: braid_fetch} = require('braid-http')
|
|
6
|
-
const defineTests = require('./tests.js')
|
|
7
|
-
|
|
8
|
-
// Parse command line arguments
|
|
9
|
-
const args = process.argv.slice(2)
|
|
10
|
-
const mode = args.includes('--browser') || args.includes('-b') ? 'browser' : 'console'
|
|
11
|
-
const portArg = args.find(arg => arg.startsWith('--port='))?.split('=')[1]
|
|
12
|
-
|| args.find(arg => !arg.startsWith('-') && !isNaN(arg))
|
|
13
|
-
const port = portArg || 8889
|
|
14
|
-
const filterArg = args.find(arg => arg.startsWith('--filter='))?.split('=')[1]
|
|
15
|
-
|| args.find(arg => arg.startsWith('--grep='))?.split('=')[1]
|
|
16
|
-
|
|
17
|
-
// Show help if requested
|
|
18
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
19
|
-
console.log(`
|
|
20
|
-
Usage: node test.js [options]
|
|
21
|
-
|
|
22
|
-
Options:
|
|
23
|
-
--browser, -b Start server for browser testing (default: console mode)
|
|
24
|
-
--port=PORT Specify port number (default: 8889)
|
|
25
|
-
PORT Port number as positional argument
|
|
26
|
-
--filter=PATTERN Only run tests matching pattern (case-insensitive)
|
|
27
|
-
--grep=PATTERN Alias for --filter
|
|
28
|
-
--help, -h Show this help message
|
|
29
|
-
|
|
30
|
-
Examples:
|
|
31
|
-
node test.js # Run all tests in console
|
|
32
|
-
node test.js --filter="sync" # Run only tests with "sync" in name
|
|
33
|
-
node test.js --grep="digest" # Run only tests with "digest" in name
|
|
34
|
-
node test.js --browser # Start browser test server
|
|
35
|
-
node test.js --browser --port=9000
|
|
36
|
-
node test.js -b 9000 # Short form with port
|
|
37
|
-
`)
|
|
38
|
-
process.exit(0)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ============================================================================
|
|
42
|
-
// Shared Server Code
|
|
43
|
-
// ============================================================================
|
|
44
|
-
|
|
45
|
-
function createTestServer(options = {}) {
|
|
46
|
-
const {
|
|
47
|
-
port = 8889,
|
|
48
|
-
runTests = false,
|
|
49
|
-
logRequests = false
|
|
50
|
-
} = options
|
|
51
|
-
|
|
52
|
-
const braid_text = require(`${__dirname}/../index.js`)
|
|
53
|
-
braid_text.db_folder = `${__dirname}/test_db_folder`
|
|
54
|
-
|
|
55
|
-
const braid_text2 = braid_text.create_braid_text()
|
|
56
|
-
braid_text2.db_folder = null
|
|
57
|
-
|
|
58
|
-
const server = http.createServer(async (req, res) => {
|
|
59
|
-
if (logRequests) {
|
|
60
|
-
console.log(`${req.method} ${req.url}`)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Free the CORS
|
|
64
|
-
braid_text.free_cors(res)
|
|
65
|
-
if (req.method === 'OPTIONS') return
|
|
66
|
-
|
|
67
|
-
if (req.url.startsWith('/have_error')) {
|
|
68
|
-
res.statusCode = 569
|
|
69
|
-
return res.end('error')
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (req.url.startsWith('/unauthorized') && req.method === 'PUT') {
|
|
73
|
-
res.statusCode = 401
|
|
74
|
-
return res.end('Unauthorized')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (req.url.startsWith('/forbidden') && req.method === 'PUT') {
|
|
78
|
-
res.statusCode = 403
|
|
79
|
-
return res.end('Forbidden')
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (req.url.startsWith('/server_error') && req.method === 'PUT') {
|
|
83
|
-
res.statusCode = 500
|
|
84
|
-
return res.end('Internal Server Error')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (req.url.startsWith('/404')) {
|
|
88
|
-
res.statusCode = 404
|
|
89
|
-
return res.end('Not Found')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (req.url.startsWith('/eval')) {
|
|
93
|
-
var body = await new Promise(done => {
|
|
94
|
-
var chunks = []
|
|
95
|
-
req.on('data', chunk => chunks.push(chunk))
|
|
96
|
-
req.on('end', () => done(Buffer.concat(chunks)))
|
|
97
|
-
})
|
|
98
|
-
try {
|
|
99
|
-
eval('' + body)
|
|
100
|
-
} catch (error) {
|
|
101
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' })
|
|
102
|
-
res.end(`Error: ${error.message}`)
|
|
103
|
-
}
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (req.url.startsWith('/test.html')) {
|
|
108
|
-
let parts = req.url.split(/[\?&=]/g)
|
|
109
|
-
|
|
110
|
-
if (parts[1] === 'check') {
|
|
111
|
-
res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache" })
|
|
112
|
-
return res.end(JSON.stringify({
|
|
113
|
-
checking: parts[2],
|
|
114
|
-
result: (await braid_text.get(parts[2])) != null
|
|
115
|
-
}))
|
|
116
|
-
} else if (parts[1] === 'dt_create_bytes_big_name') {
|
|
117
|
-
try {
|
|
118
|
-
braid_text.dt_create_bytes('x'.repeat(1000000) + '-0', [], 0, 0, 'hi')
|
|
119
|
-
return res.end(JSON.stringify({ ok: true }))
|
|
120
|
-
} catch (e) {
|
|
121
|
-
return res.end(JSON.stringify({ ok: false, error: '' + e }))
|
|
122
|
-
}
|
|
123
|
-
} else if (parts[1] === 'dt_create_bytes_many_names') {
|
|
124
|
-
try {
|
|
125
|
-
braid_text.dt_create_bytes('hi-0', new Array(1000000).fill(0).map((x, i) => `x${i}-0`), 0, 0, 'hi')
|
|
126
|
-
return res.end(JSON.stringify({ ok: true }))
|
|
127
|
-
} catch (e) {
|
|
128
|
-
return res.end(JSON.stringify({ ok: false, error: '' + e }))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-cache" })
|
|
133
|
-
require("fs").createReadStream(`${__dirname}/test.html`).pipe(res)
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Serve tests.js file for browser
|
|
138
|
-
if (req.url.startsWith('/tests.js')) {
|
|
139
|
-
res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8", "Cache-Control": "no-cache" })
|
|
140
|
-
require("fs").createReadStream(`${__dirname}/tests.js`).pipe(res)
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Now serve the collaborative text!
|
|
145
|
-
braid_text.serve(req, res)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
server,
|
|
150
|
-
start: () => new Promise((resolve) => {
|
|
151
|
-
server.listen(port, 'localhost', () => {
|
|
152
|
-
if (runTests) {
|
|
153
|
-
console.log(`Test server running on http://localhost:${port}`)
|
|
154
|
-
} else {
|
|
155
|
-
console.log(`serving: http://localhost:${port}/test.html`)
|
|
156
|
-
}
|
|
157
|
-
resolve()
|
|
158
|
-
})
|
|
159
|
-
}),
|
|
160
|
-
port
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ============================================================================
|
|
165
|
-
// Console Test Mode (Node.js)
|
|
166
|
-
// ============================================================================
|
|
167
|
-
|
|
168
|
-
async function runConsoleTests() {
|
|
169
|
-
// Test tracking
|
|
170
|
-
let totalTests = 0
|
|
171
|
-
let passedTests = 0
|
|
172
|
-
let failedTests = 0
|
|
173
|
-
|
|
174
|
-
// Handle unhandled rejections during tests (some tests intentionally cause errors)
|
|
175
|
-
const unhandledRejections = []
|
|
176
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
177
|
-
// Collect but don't crash - some tests intentionally trigger errors
|
|
178
|
-
unhandledRejections.push({ reason, promise })
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
// Node.js test runner implementation
|
|
182
|
-
// Store tests to run sequentially instead of in parallel
|
|
183
|
-
const testsToRun = []
|
|
184
|
-
|
|
185
|
-
function runTest(testName, testFunction, expectedResult) {
|
|
186
|
-
// Apply filter if specified
|
|
187
|
-
if (filterArg && !testName.toLowerCase().includes(filterArg.toLowerCase())) {
|
|
188
|
-
return // Skip this test
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
totalTests++
|
|
192
|
-
testsToRun.push({ testName, testFunction, expectedResult })
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Create a braid_fetch wrapper that points to localhost
|
|
196
|
-
function createBraidFetch(baseUrl) {
|
|
197
|
-
return async (url, options = {}) => {
|
|
198
|
-
const fullUrl = url.startsWith('http') ? url : `${baseUrl}${url}`
|
|
199
|
-
return braid_fetch(fullUrl, options)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
console.log('Starting braid-text tests...\n')
|
|
204
|
-
|
|
205
|
-
// Create and start the test server
|
|
206
|
-
const testServer = createTestServer({
|
|
207
|
-
port,
|
|
208
|
-
runTests: true,
|
|
209
|
-
logRequests: false
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
await testServer.start()
|
|
213
|
-
|
|
214
|
-
// Create braid_fetch bound to test server
|
|
215
|
-
const testBraidFetch = createBraidFetch(`http://localhost:${port}`)
|
|
216
|
-
|
|
217
|
-
// Load the real diamond-types module for Node.js
|
|
218
|
-
const { Doc, OpLog } = require('@braid.org/diamond-types-node')
|
|
219
|
-
|
|
220
|
-
// Define globals needed for some tests
|
|
221
|
-
global.Doc = Doc
|
|
222
|
-
global.OpLog = OpLog
|
|
223
|
-
global.dt_p = Promise.resolve() // No initialization needed for Node.js version
|
|
224
|
-
global.fetch = testBraidFetch
|
|
225
|
-
global.AbortController = AbortController
|
|
226
|
-
global.crypto = require('crypto').webcrypto
|
|
227
|
-
|
|
228
|
-
// Run all tests
|
|
229
|
-
defineTests(runTest, testBraidFetch)
|
|
230
|
-
|
|
231
|
-
// Run tests sequentially (not in parallel) to avoid conflicts
|
|
232
|
-
for (const { testName, testFunction, expectedResult } of testsToRun) {
|
|
233
|
-
try {
|
|
234
|
-
const result = await testFunction()
|
|
235
|
-
if (result == expectedResult) {
|
|
236
|
-
passedTests++
|
|
237
|
-
console.log(`✓ ${testName}`)
|
|
238
|
-
} else {
|
|
239
|
-
failedTests++
|
|
240
|
-
console.log(`✗ ${testName}`)
|
|
241
|
-
console.log(` Expected: ${expectedResult}`)
|
|
242
|
-
console.log(` Got: ${result}`)
|
|
243
|
-
}
|
|
244
|
-
} catch (error) {
|
|
245
|
-
failedTests++
|
|
246
|
-
console.log(`✗ ${testName}`)
|
|
247
|
-
console.log(` Error: ${error.message || error}`)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Print summary
|
|
252
|
-
console.log('\n' + '='.repeat(50))
|
|
253
|
-
console.log(`Total: ${totalTests} | Passed: ${passedTests} | Failed: ${failedTests}`)
|
|
254
|
-
console.log('='.repeat(50))
|
|
255
|
-
|
|
256
|
-
// Clean up test database folder
|
|
257
|
-
console.log('Cleaning up test database folder...')
|
|
258
|
-
const fs = require('fs')
|
|
259
|
-
const path = require('path')
|
|
260
|
-
const testDbPath = path.join(__dirname, 'test_db_folder')
|
|
261
|
-
try {
|
|
262
|
-
if (fs.existsSync(testDbPath)) {
|
|
263
|
-
fs.rmSync(testDbPath, { recursive: true, force: true })
|
|
264
|
-
console.log('Test database folder removed')
|
|
265
|
-
}
|
|
266
|
-
} catch (err) {
|
|
267
|
-
console.log(`Warning: Could not remove test database folder: ${err.message}`)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Force close the server and all connections
|
|
271
|
-
console.log('Closing server...')
|
|
272
|
-
testServer.server.close(() => {
|
|
273
|
-
console.log('Server closed callback - calling process.exit()')
|
|
274
|
-
process.exit(failedTests > 0 ? 1 : 0)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
// Also close all active connections if the method exists (Node 18.2+)
|
|
278
|
-
if (typeof testServer.server.closeAllConnections === 'function') {
|
|
279
|
-
console.log('Closing all connections...')
|
|
280
|
-
testServer.server.closeAllConnections()
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Fallback: force exit after a short delay even if server hasn't fully closed
|
|
284
|
-
console.log('Setting 200ms timeout fallback...')
|
|
285
|
-
setTimeout(() => {
|
|
286
|
-
console.log('Timeout reached - calling process.exit()')
|
|
287
|
-
process.exit(failedTests > 0 ? 1 : 0)
|
|
288
|
-
}, 200)
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// ============================================================================
|
|
292
|
-
// Browser Test Mode (Server)
|
|
293
|
-
// ============================================================================
|
|
294
|
-
|
|
295
|
-
async function runBrowserMode() {
|
|
296
|
-
const testServer = createTestServer({
|
|
297
|
-
port,
|
|
298
|
-
runTests: false,
|
|
299
|
-
logRequests: true
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
await testServer.start()
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// ============================================================================
|
|
306
|
-
// Main Entry Point
|
|
307
|
-
// ============================================================================
|
|
308
|
-
|
|
309
|
-
async function main() {
|
|
310
|
-
if (mode === 'browser') {
|
|
311
|
-
await runBrowserMode()
|
|
312
|
-
} else {
|
|
313
|
-
await runConsoleTests()
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Run the appropriate mode
|
|
318
|
-
main().catch(err => {
|
|
319
|
-
console.error('Fatal error:', err)
|
|
320
|
-
process.exit(1)
|
|
321
|
-
})
|