@pikku/bun-server 0.12.11 → 0.12.13

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.
@@ -1,193 +0,0 @@
1
- import { describe, test, before, after } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import {
4
- mkdtempSync,
5
- mkdirSync,
6
- writeFileSync,
7
- readFileSync,
8
- rmSync,
9
- } from 'node:fs'
10
- import { tmpdir } from 'node:os'
11
- import { join } from 'node:path'
12
- import { setSingletonServices } from '@pikku/core/state'
13
- import { resetPikkuState } from '@pikku/core/state'
14
- import { LocalContent } from '@pikku/core/services/local-content'
15
- import type { JWTService, Logger } from '@pikku/core/services'
16
- import { PikkuBunServer } from './pikku-bun-server.js'
17
-
18
- const noopLogger = {
19
- info: () => {},
20
- error: () => {},
21
- warn: () => {},
22
- debug: () => {},
23
- trace: () => {},
24
- setLevel: () => {},
25
- } as unknown as Logger
26
-
27
- /**
28
- * Signs and verifies with a plain base64 round-trip. The point of these tests is
29
- * that the server checks the SAME claims the signer wrote, not that any
30
- * particular algorithm is used — a real JWT here would only slow them down.
31
- */
32
- const fakeJWT = {
33
- encode: async (_expiry: string, payload: unknown) =>
34
- Buffer.from(JSON.stringify(payload)).toString('base64url'),
35
- decode: async (token: string) =>
36
- JSON.parse(Buffer.from(token, 'base64url').toString()),
37
- } as unknown as JWTService
38
-
39
- describe('PikkuBunServer local content', () => {
40
- let server: PikkuBunServer
41
- let content: LocalContent
42
- let tmpDir: string
43
- let origin: string
44
-
45
- before(async () => {
46
- resetPikkuState()
47
- setSingletonServices({
48
- logger: noopLogger,
49
- schema: {
50
- compileSchema: () => {},
51
- getSchemaNames: () => new Set<string>(),
52
- },
53
- } as any)
54
- tmpDir = mkdtempSync(join(tmpdir(), 'pikku-bun-content-'))
55
- mkdirSync(join(tmpDir, 'bucket'), { recursive: true })
56
- writeFileSync(join(tmpDir, 'bucket', 'existing.bin'), 'already here')
57
-
58
- const contentConfig = {
59
- localFileUploadPath: tmpDir,
60
- uploadUrlPrefix: '/upload',
61
- assetUrlPrefix: '/assets',
62
- sizeLimit: '1mb',
63
- }
64
- // The service under test on the client side: the URLs it hands out are
65
- // exactly what the server must answer, so they are not hand-written here.
66
- content = new LocalContent(contentConfig, noopLogger, fakeJWT)
67
-
68
- server = new PikkuBunServer(
69
- {
70
- port: 0,
71
- hostname: '127.0.0.1',
72
- content: contentConfig,
73
- } as any,
74
- noopLogger,
75
- { contentSigningJWT: fakeJWT }
76
- )
77
- await server.init()
78
- await server.start()
79
- origin = `http://127.0.0.1:${server.port}`
80
- })
81
-
82
- after(async () => {
83
- await server.stop()
84
- rmSync(tmpDir, { recursive: true, force: true })
85
- })
86
-
87
- test('answers the upload URL the content service hands out', async () => {
88
- const { uploadUrl } = await content.getUploadURL({
89
- bucket: 'bucket',
90
- fileKey: 'nested/file.bin',
91
- contentType: 'application/octet-stream',
92
- } as any)
93
-
94
- const response = await fetch(`${origin}${uploadUrl}`, {
95
- method: 'PUT',
96
- body: 'uploaded bytes',
97
- })
98
-
99
- assert.equal(response.status, 200)
100
- assert.equal(
101
- readFileSync(join(tmpDir, 'bucket', 'nested', 'file.bin'), 'utf8'),
102
- 'uploaded bytes'
103
- )
104
- })
105
-
106
- test('serves an asset through the signed URL the content service produces', async () => {
107
- const signed = await content.signContentKey({
108
- bucket: 'bucket',
109
- contentKey: 'existing.bin',
110
- dateLessThan: new Date(Date.now() + 60_000),
111
- } as any)
112
-
113
- const response = await fetch(`${origin}${signed}`)
114
- assert.equal(response.status, 200)
115
- assert.equal(await response.text(), 'already here')
116
- })
117
-
118
- test('refuses an unsigned asset read', async () => {
119
- const response = await fetch(`${origin}/assets/bucket/existing.bin`)
120
- assert.equal(response.status, 403)
121
- })
122
-
123
- test('refuses a signature minted for a different asset', async () => {
124
- // The signature is valid and unexpired — only the path differs. Without the
125
- // path claim being checked, one signed URL would read the whole bucket.
126
- const signed = await content.signContentKey({
127
- bucket: 'bucket',
128
- contentKey: 'existing.bin',
129
- dateLessThan: new Date(Date.now() + 60_000),
130
- } as any)
131
- writeFileSync(join(tmpDir, 'bucket', 'other.bin'), 'other')
132
-
133
- const query = signed.slice(signed.indexOf('?'))
134
- const response = await fetch(`${origin}/assets/bucket/other.bin${query}`)
135
- assert.equal(response.status, 403)
136
- })
137
-
138
- test('refuses an expired signature', async () => {
139
- const signed = await content.signContentKey({
140
- bucket: 'bucket',
141
- contentKey: 'existing.bin',
142
- dateLessThan: new Date(Date.now() - 1_000),
143
- } as any)
144
-
145
- const response = await fetch(`${origin}${signed}`)
146
- assert.equal(response.status, 403)
147
- })
148
-
149
- // `fetch` normalises `..` out of the path before the request leaves the
150
- // client, so this cannot drive the guard itself — that is covered directly in
151
- // core's local-content-request-handler.test.ts. What it does prove is that
152
- // nothing in the server path un-normalises it back into a write.
153
- test('does not write outside the content directory', async () => {
154
- writeFileSync(join(tmpDir, '..', 'pikku-bun-content-secret.txt'), 'secret')
155
- try {
156
- const response = await fetch(
157
- `${origin}/upload/%2e%2e/pikku-bun-content-secret.txt`,
158
- { method: 'PUT', body: 'overwritten' }
159
- )
160
- assert.notEqual(response.status, 200)
161
- assert.equal(
162
- readFileSync(
163
- join(tmpDir, '..', 'pikku-bun-content-secret.txt'),
164
- 'utf8'
165
- ),
166
- 'secret'
167
- )
168
- } finally {
169
- rmSync(join(tmpDir, '..', 'pikku-bun-content-secret.txt'), {
170
- force: true,
171
- })
172
- }
173
- })
174
-
175
- test('rejects an upload over the size limit', async () => {
176
- const { uploadUrl } = await content.getUploadURL({
177
- bucket: 'bucket',
178
- fileKey: 'too-big.bin',
179
- contentType: 'application/octet-stream',
180
- } as any)
181
-
182
- const response = await fetch(`${origin}${uploadUrl}`, {
183
- method: 'PUT',
184
- body: Buffer.alloc(1024 * 1024 + 1),
185
- })
186
- assert.equal(response.status, 413)
187
- })
188
-
189
- test('leaves non-content paths to the router', async () => {
190
- const response = await fetch(`${origin}/uploadsomething`)
191
- assert.equal(response.status, 404)
192
- })
193
- })
@@ -1,62 +0,0 @@
1
- import { describe, test, before, after } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { setSingletonServices } from '@pikku/core/state'
4
- import { resetPikkuState } from '@pikku/core/state'
5
- import type { Logger } from '@pikku/core/services'
6
- import { PikkuBunServer } from './pikku-bun-server.js'
7
-
8
- const PORT = 47817
9
- const HEALTH = '/__health'
10
-
11
- const noopLogger = {
12
- info: () => {},
13
- error: () => {},
14
- warn: () => {},
15
- debug: () => {},
16
- trace: () => {},
17
- setLevel: () => {},
18
- } as unknown as Logger
19
-
20
- describe('PikkuBunServer', () => {
21
- let server: PikkuBunServer
22
-
23
- before(async () => {
24
- setSingletonServices({
25
- logger: noopLogger,
26
- schema: {
27
- compileSchema: () => {},
28
- getSchemaNames: () => new Set<string>(),
29
- },
30
- } as any)
31
- server = new PikkuBunServer(
32
- { port: PORT, hostname: 'localhost', healthCheckPath: HEALTH },
33
- noopLogger
34
- )
35
- await server.init()
36
- await server.start()
37
- })
38
-
39
- after(async () => {
40
- await server.stop()
41
- resetPikkuState()
42
- })
43
-
44
- test('serves the configured health-check path', async () => {
45
- const res = await fetch(`http://localhost:${PORT}${HEALTH}`)
46
- assert.equal(res.status, 200)
47
- assert.equal(res.headers.get('content-type'), 'application/json')
48
- assert.deepEqual(await res.json(), { ok: true })
49
- })
50
-
51
- test('returns 404 for unregistered routes', async () => {
52
- const res = await fetch(`http://localhost:${PORT}/nothing/here`)
53
- assert.equal(res.status, 404)
54
- })
55
-
56
- test('stop() is idempotent', async () => {
57
- const extra = new PikkuBunServer({ port: PORT + 1 }, noopLogger)
58
- await extra.start()
59
- await extra.stop()
60
- await assert.doesNotReject(extra.stop())
61
- })
62
- })