javascript-solid-server 0.0.7 → 0.0.8

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,289 @@
1
+ /**
2
+ * Content Negotiation Tests
3
+ *
4
+ * Tests Turtle <-> JSON-LD conversion with conneg enabled.
5
+ * Note: Content negotiation is OFF by default (JSON-LD native server).
6
+ */
7
+
8
+ import { describe, it, before, after } from 'node:test';
9
+ import assert from 'node:assert';
10
+ import {
11
+ startTestServer,
12
+ stopTestServer,
13
+ request,
14
+ createTestPod,
15
+ assertStatus,
16
+ assertHeader,
17
+ assertHeaderContains
18
+ } from './helpers.js';
19
+
20
+ describe('Content Negotiation (conneg enabled)', () => {
21
+ before(async () => {
22
+ // Start server with conneg ENABLED
23
+ await startTestServer({ conneg: true });
24
+ await createTestPod('connegtest');
25
+ });
26
+
27
+ after(async () => {
28
+ await stopTestServer();
29
+ });
30
+
31
+ describe('GET with Accept header', () => {
32
+ it('should return JSON-LD when Accept: application/ld+json', async () => {
33
+ // Create a JSON-LD resource
34
+ const data = {
35
+ '@context': { 'foaf': 'http://xmlns.com/foaf/0.1/' },
36
+ '@id': '#me',
37
+ 'foaf:name': 'Alice'
38
+ };
39
+
40
+ await request('/connegtest/public/alice.json', {
41
+ method: 'PUT',
42
+ headers: { 'Content-Type': 'application/ld+json' },
43
+ body: JSON.stringify(data),
44
+ auth: 'connegtest'
45
+ });
46
+
47
+ const res = await request('/connegtest/public/alice.json', {
48
+ headers: { 'Accept': 'application/ld+json' }
49
+ });
50
+
51
+ assertStatus(res, 200);
52
+ assertHeaderContains(res, 'Content-Type', 'application/ld+json');
53
+
54
+ const body = await res.json();
55
+ assert.strictEqual(body['foaf:name'], 'Alice');
56
+ });
57
+
58
+ it('should return Turtle when Accept: text/turtle', async () => {
59
+ // Create a JSON-LD resource
60
+ const data = {
61
+ '@context': { 'foaf': 'http://xmlns.com/foaf/0.1/' },
62
+ '@id': '#me',
63
+ 'foaf:name': 'Bob'
64
+ };
65
+
66
+ await request('/connegtest/public/bob.json', {
67
+ method: 'PUT',
68
+ headers: { 'Content-Type': 'application/ld+json' },
69
+ body: JSON.stringify(data),
70
+ auth: 'connegtest'
71
+ });
72
+
73
+ const res = await request('/connegtest/public/bob.json', {
74
+ headers: { 'Accept': 'text/turtle' }
75
+ });
76
+
77
+ assertStatus(res, 200);
78
+ assertHeaderContains(res, 'Content-Type', 'text/turtle');
79
+
80
+ const turtle = await res.text();
81
+ // Should contain foaf prefix and name
82
+ assert.ok(turtle.includes('foaf:') || turtle.includes('http://xmlns.com/foaf/0.1/'),
83
+ 'Turtle should contain foaf prefix or URI');
84
+ assert.ok(turtle.includes('Bob'), 'Turtle should contain the name');
85
+ });
86
+
87
+ it('should default to JSON-LD for */* Accept', async () => {
88
+ const res = await request('/connegtest/public/alice.json', {
89
+ headers: { 'Accept': '*/*' }
90
+ });
91
+
92
+ assertStatus(res, 200);
93
+ assertHeaderContains(res, 'Content-Type', 'application/ld+json');
94
+ });
95
+
96
+ it('should include Vary header with Accept', async () => {
97
+ const res = await request('/connegtest/public/alice.json');
98
+ const vary = res.headers.get('Vary');
99
+ assert.ok(vary && vary.includes('Accept'), 'Should have Vary: Accept');
100
+ });
101
+ });
102
+
103
+ describe('PUT with Content-Type', () => {
104
+ it('should accept Turtle input and store as JSON-LD', async () => {
105
+ const turtle = `
106
+ @prefix foaf: <http://xmlns.com/foaf/0.1/>.
107
+ <#me> foaf:name "Charlie".
108
+ `;
109
+
110
+ const res = await request('/connegtest/public/charlie.json', {
111
+ method: 'PUT',
112
+ headers: { 'Content-Type': 'text/turtle' },
113
+ body: turtle,
114
+ auth: 'connegtest'
115
+ });
116
+
117
+ assertStatus(res, 201);
118
+
119
+ // Verify it's stored as JSON-LD
120
+ const getRes = await request('/connegtest/public/charlie.json', {
121
+ headers: { 'Accept': 'application/ld+json' }
122
+ });
123
+
124
+ assertStatus(getRes, 200);
125
+ const data = await getRes.json();
126
+ assert.ok(data['@context'], 'Should have @context');
127
+ });
128
+
129
+ it('should accept N3 input', async () => {
130
+ const n3 = `
131
+ @prefix schema: <http://schema.org/>.
132
+ <#item> schema:name "Widget".
133
+ `;
134
+
135
+ const res = await request('/connegtest/public/widget.json', {
136
+ method: 'PUT',
137
+ headers: { 'Content-Type': 'text/n3' },
138
+ body: n3,
139
+ auth: 'connegtest'
140
+ });
141
+
142
+ assertStatus(res, 201);
143
+ });
144
+
145
+ it('should return 400 for invalid Turtle', async () => {
146
+ const invalidTurtle = 'this is not valid turtle {{{';
147
+
148
+ const res = await request('/connegtest/public/invalid.json', {
149
+ method: 'PUT',
150
+ headers: { 'Content-Type': 'text/turtle' },
151
+ body: invalidTurtle,
152
+ auth: 'connegtest'
153
+ });
154
+
155
+ assertStatus(res, 400);
156
+ });
157
+ });
158
+
159
+ describe('POST with Content-Type', () => {
160
+ it('should accept Turtle input in POST', async () => {
161
+ const turtle = `
162
+ @prefix dc: <http://purl.org/dc/terms/>.
163
+ <#doc> dc:title "My Document".
164
+ `;
165
+
166
+ const res = await request('/connegtest/public/', {
167
+ method: 'POST',
168
+ headers: {
169
+ 'Content-Type': 'text/turtle',
170
+ 'Slug': 'turtle-doc.json'
171
+ },
172
+ body: turtle,
173
+ auth: 'connegtest'
174
+ });
175
+
176
+ assertStatus(res, 201);
177
+ const location = res.headers.get('Location');
178
+ assert.ok(location, 'Should have Location header');
179
+ });
180
+ });
181
+
182
+ describe('Accept-* Headers', () => {
183
+ it('should advertise Turtle support in Accept-Put', async () => {
184
+ const res = await request('/connegtest/public/alice.json');
185
+ const acceptPut = res.headers.get('Accept-Put');
186
+ assert.ok(acceptPut && acceptPut.includes('text/turtle'),
187
+ 'Accept-Put should include text/turtle');
188
+ });
189
+
190
+ it('should advertise Turtle support in Accept-Post for containers', async () => {
191
+ const res = await request('/connegtest/public/');
192
+ const acceptPost = res.headers.get('Accept-Post');
193
+ assert.ok(acceptPost && acceptPost.includes('text/turtle'),
194
+ 'Accept-Post should include text/turtle');
195
+ });
196
+ });
197
+ });
198
+
199
+ describe('Content Negotiation (conneg disabled - default)', () => {
200
+ before(async () => {
201
+ // Start server with conneg DISABLED (default)
202
+ await startTestServer({ conneg: false });
203
+ await createTestPod('noconneg');
204
+ });
205
+
206
+ after(async () => {
207
+ await stopTestServer();
208
+ });
209
+
210
+ describe('Default JSON-LD behavior', () => {
211
+ it('should always return JSON-LD regardless of Accept header', async () => {
212
+ // Create resource
213
+ const data = {
214
+ '@context': { 'foaf': 'http://xmlns.com/foaf/0.1/' },
215
+ '@id': '#me',
216
+ 'foaf:name': 'DefaultUser'
217
+ };
218
+
219
+ await request('/noconneg/public/user.json', {
220
+ method: 'PUT',
221
+ headers: { 'Content-Type': 'application/ld+json' },
222
+ body: JSON.stringify(data),
223
+ auth: 'noconneg'
224
+ });
225
+
226
+ // Request Turtle
227
+ const res = await request('/noconneg/public/user.json', {
228
+ headers: { 'Accept': 'text/turtle' }
229
+ });
230
+
231
+ assertStatus(res, 200);
232
+ // Should still return JSON-LD when conneg disabled
233
+ const body = await res.json();
234
+ assert.strictEqual(body['foaf:name'], 'DefaultUser');
235
+ });
236
+
237
+ it('should accept JSON-LD input', async () => {
238
+ const data = { '@id': '#test', 'http://example.org/p': 'value' };
239
+
240
+ const res = await request('/noconneg/public/test.json', {
241
+ method: 'PUT',
242
+ headers: { 'Content-Type': 'application/ld+json' },
243
+ body: JSON.stringify(data),
244
+ auth: 'noconneg'
245
+ });
246
+
247
+ assertStatus(res, 201);
248
+ });
249
+
250
+ it('should accept plain JSON input', async () => {
251
+ const data = { foo: 'bar' };
252
+
253
+ const res = await request('/noconneg/public/plain.json', {
254
+ method: 'PUT',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify(data),
257
+ auth: 'noconneg'
258
+ });
259
+
260
+ assertStatus(res, 201);
261
+ });
262
+
263
+ it('should accept non-RDF content types', async () => {
264
+ const res = await request('/noconneg/public/readme.txt', {
265
+ method: 'PUT',
266
+ headers: { 'Content-Type': 'text/plain' },
267
+ body: 'Hello World',
268
+ auth: 'noconneg'
269
+ });
270
+
271
+ assertStatus(res, 201);
272
+
273
+ const getRes = await request('/noconneg/public/readme.txt');
274
+ assertStatus(getRes, 200);
275
+ const text = await getRes.text();
276
+ assert.strictEqual(text, 'Hello World');
277
+ });
278
+
279
+ it('should not advertise Turtle in Accept-Put when conneg disabled', async () => {
280
+ const res = await request('/noconneg/public/');
281
+ const acceptPut = res.headers.get('Accept-Put');
282
+ // Should only advertise JSON-LD, not Turtle
283
+ assert.ok(acceptPut && acceptPut.includes('application/ld+json'),
284
+ 'Accept-Put should include application/ld+json');
285
+ assert.ok(!acceptPut || !acceptPut.includes('text/turtle'),
286
+ 'Accept-Put should NOT include text/turtle when conneg disabled');
287
+ });
288
+ });
289
+ });
package/test/helpers.js CHANGED
@@ -16,13 +16,15 @@ const podTokens = new Map();
16
16
 
17
17
  /**
18
18
  * Start a test server on a random available port
19
+ * @param {object} options - Server options
20
+ * @param {boolean} options.conneg - Enable content negotiation (default false)
19
21
  * @returns {Promise<{server: object, baseUrl: string}>}
20
22
  */
21
- export async function startTestServer() {
23
+ export async function startTestServer(options = {}) {
22
24
  // Clean up any existing test data
23
25
  await fs.emptyDir(TEST_DATA_DIR);
24
26
 
25
- server = createServer({ logger: false });
27
+ server = createServer({ logger: false, ...options });
26
28
  // Use port 0 to let OS assign available port
27
29
  await server.listen({ port: 0, host: '127.0.0.1' });
28
30