fedbox 0.0.4 → 0.0.5

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/lib/server.js CHANGED
@@ -383,7 +383,7 @@ async function handleRequest(req, res) {
383
383
  version: '2.1',
384
384
  software: {
385
385
  name: 'fedbox',
386
- version: '0.0.4',
386
+ version: '0.0.5',
387
387
  repository: 'https://github.com/micro-fed/fedbox'
388
388
  },
389
389
  protocols: ['activitypub'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fedbox",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Zero to Fediverse in 60 seconds",
5
5
  "type": "module",
6
6
  "main": "lib/server.js",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node lib/server.js",
12
- "test": "node --test test/"
12
+ "test": "node --test 'test/*.test.js'"
13
13
  },
14
14
  "dependencies": {
15
15
  "microfed": "^0.0.13",
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Actions tests
3
+ */
4
+
5
+ import { test, describe, before, after } from 'node:test'
6
+ import assert from 'node:assert'
7
+ import { writeFileSync, unlinkSync, existsSync, mkdirSync, rmSync } from 'fs'
8
+ import { generateKeypair } from 'microfed/auth'
9
+
10
+ function setupConfig(domain = null) {
11
+ const { publicKey, privateKey } = generateKeypair()
12
+ const config = {
13
+ username: 'alice',
14
+ displayName: 'Alice',
15
+ summary: 'Test user',
16
+ port: 3000,
17
+ publicKey,
18
+ privateKey
19
+ }
20
+ if (domain) config.domain = domain
21
+ writeFileSync('fedbox.json', JSON.stringify(config, null, 2))
22
+ }
23
+
24
+ function cleanup() {
25
+ if (existsSync('fedbox.json')) unlinkSync('fedbox.json')
26
+ if (existsSync('data/fedbox.db')) unlinkSync('data/fedbox.db')
27
+ try { rmSync('data', { recursive: true }) } catch {}
28
+ }
29
+
30
+ describe('Actions', () => {
31
+ before(() => {
32
+ cleanup()
33
+ if (!existsSync('data')) mkdirSync('data')
34
+ setupConfig()
35
+ })
36
+
37
+ after(() => {
38
+ cleanup()
39
+ })
40
+
41
+ test('post creates note with Solid-compatible noteId', async () => {
42
+ const { post } = await import('../lib/actions.js')
43
+ const result = await post('Hello world!')
44
+
45
+ // noteId should be /alice/posts/xxx (not /alice#me/posts/xxx)
46
+ assert.ok(result.noteId.includes('/alice/posts/'), 'noteId should include /alice/posts/')
47
+ assert.ok(!result.noteId.includes('#me/posts'), 'noteId should not have #me before /posts')
48
+ assert.ok(!result.noteId.includes('#me'), 'noteId should not contain #me at all')
49
+ })
50
+
51
+ test('post returns delivery results', async () => {
52
+ const { post } = await import('../lib/actions.js')
53
+ const result = await post('Test delivery')
54
+
55
+ assert.ok('delivered' in result, 'result should have delivered stats')
56
+ assert.ok('success' in result.delivered)
57
+ assert.ok('failed' in result.delivered)
58
+ })
59
+
60
+ test('myPosts returns saved posts', async () => {
61
+ const { myPosts } = await import('../lib/actions.js')
62
+ const posts = myPosts(10)
63
+ // Should have posts from previous tests
64
+ assert.ok(posts.length >= 1, 'should have at least 1 post')
65
+ assert.ok(posts[0].content, 'post should have content')
66
+ assert.ok(posts[0].id, 'post should have id')
67
+ })
68
+
69
+ test('myPosts posts have Solid-compatible URLs', async () => {
70
+ const { myPosts } = await import('../lib/actions.js')
71
+ const posts = myPosts(10)
72
+
73
+ for (const post of posts) {
74
+ assert.ok(post.id.includes('/alice/posts/'), 'post id should include /alice/posts/')
75
+ assert.ok(!post.id.includes('#me'), 'post id should not contain #me')
76
+ }
77
+ })
78
+ })
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Store tests
3
+ */
4
+
5
+ import { test, describe, beforeEach, afterEach } from 'node:test'
6
+ import assert from 'node:assert'
7
+ import { unlinkSync, existsSync, mkdirSync } from 'fs'
8
+ import {
9
+ initStore,
10
+ getStore,
11
+ addFollower,
12
+ removeFollower,
13
+ getFollowers,
14
+ getFollowerCount,
15
+ addFollowing,
16
+ acceptFollowing,
17
+ getFollowing,
18
+ getFollowingCount,
19
+ savePost,
20
+ getPosts,
21
+ getPost,
22
+ cacheActor,
23
+ getCachedActor,
24
+ saveActivity,
25
+ getActivities
26
+ } from '../lib/store.js'
27
+
28
+ const TEST_DB = 'test/test.db'
29
+
30
+ function cleanup() {
31
+ if (existsSync(TEST_DB)) {
32
+ unlinkSync(TEST_DB)
33
+ }
34
+ }
35
+
36
+ describe('Store', () => {
37
+ beforeEach(() => {
38
+ cleanup()
39
+ if (!existsSync('test')) mkdirSync('test', { recursive: true })
40
+ initStore(TEST_DB)
41
+ })
42
+
43
+ afterEach(() => {
44
+ const db = getStore()
45
+ db.close()
46
+ cleanup()
47
+ })
48
+
49
+ test('initStore creates database', () => {
50
+ assert.ok(existsSync(TEST_DB))
51
+ })
52
+
53
+ test('getStore returns database instance', () => {
54
+ const db = getStore()
55
+ assert.ok(db)
56
+ })
57
+ })
58
+
59
+ describe('Followers', () => {
60
+ beforeEach(() => {
61
+ cleanup()
62
+ initStore(TEST_DB)
63
+ })
64
+
65
+ afterEach(() => {
66
+ const db = getStore()
67
+ db.close()
68
+ cleanup()
69
+ })
70
+
71
+ test('addFollower and getFollowers', () => {
72
+ addFollower('https://example.com/user/alice', 'https://example.com/user/alice/inbox')
73
+ const followers = getFollowers()
74
+ assert.strictEqual(followers.length, 1)
75
+ assert.strictEqual(followers[0].actor, 'https://example.com/user/alice')
76
+ assert.strictEqual(followers[0].inbox, 'https://example.com/user/alice/inbox')
77
+ })
78
+
79
+ test('getFollowerCount', () => {
80
+ assert.strictEqual(getFollowerCount(), 0)
81
+ addFollower('https://example.com/user/alice', 'https://example.com/inbox')
82
+ assert.strictEqual(getFollowerCount(), 1)
83
+ addFollower('https://example.com/user/bob', 'https://example.com/inbox')
84
+ assert.strictEqual(getFollowerCount(), 2)
85
+ })
86
+
87
+ test('removeFollower', () => {
88
+ addFollower('https://example.com/user/alice', 'https://example.com/inbox')
89
+ assert.strictEqual(getFollowerCount(), 1)
90
+ removeFollower('https://example.com/user/alice')
91
+ assert.strictEqual(getFollowerCount(), 0)
92
+ })
93
+ })
94
+
95
+ describe('Following', () => {
96
+ beforeEach(() => {
97
+ cleanup()
98
+ initStore(TEST_DB)
99
+ })
100
+
101
+ afterEach(() => {
102
+ const db = getStore()
103
+ db.close()
104
+ cleanup()
105
+ })
106
+
107
+ test('addFollowing with pending status', () => {
108
+ addFollowing('https://example.com/user/bob', false)
109
+ assert.strictEqual(getFollowingCount(), 0) // Not accepted yet
110
+ })
111
+
112
+ test('acceptFollowing', () => {
113
+ addFollowing('https://example.com/user/bob', false)
114
+ assert.strictEqual(getFollowingCount(), 0)
115
+ acceptFollowing('https://example.com/user/bob')
116
+ assert.strictEqual(getFollowingCount(), 1)
117
+ })
118
+
119
+ test('getFollowing returns only accepted', () => {
120
+ addFollowing('https://example.com/user/bob', false)
121
+ addFollowing('https://example.com/user/charlie', true)
122
+ const following = getFollowing()
123
+ assert.strictEqual(following.length, 1)
124
+ assert.strictEqual(following[0].actor, 'https://example.com/user/charlie')
125
+ })
126
+ })
127
+
128
+ describe('Posts', () => {
129
+ beforeEach(() => {
130
+ cleanup()
131
+ initStore(TEST_DB)
132
+ })
133
+
134
+ afterEach(() => {
135
+ const db = getStore()
136
+ db.close()
137
+ cleanup()
138
+ })
139
+
140
+ test('savePost and getPost', () => {
141
+ const id = 'https://example.com/alice/posts/123'
142
+ savePost(id, 'Hello world!')
143
+ const post = getPost(id)
144
+ assert.ok(post)
145
+ assert.strictEqual(post.id, id)
146
+ assert.strictEqual(post.content, 'Hello world!')
147
+ })
148
+
149
+ test('savePost with inReplyTo', () => {
150
+ const id = 'https://example.com/alice/posts/456'
151
+ const replyTo = 'https://other.com/posts/789'
152
+ savePost(id, 'This is a reply', replyTo)
153
+ const post = getPost(id)
154
+ assert.strictEqual(post.in_reply_to, replyTo)
155
+ })
156
+
157
+ test('getPosts returns posts', () => {
158
+ savePost('https://example.com/posts/1', 'First')
159
+ savePost('https://example.com/posts/2', 'Second')
160
+ savePost('https://example.com/posts/3', 'Third')
161
+ const posts = getPosts(10)
162
+ assert.strictEqual(posts.length, 3)
163
+ // All posts should be present
164
+ const contents = posts.map(p => p.content)
165
+ assert.ok(contents.includes('First'))
166
+ assert.ok(contents.includes('Second'))
167
+ assert.ok(contents.includes('Third'))
168
+ })
169
+
170
+ test('getPosts respects limit', () => {
171
+ for (let i = 0; i < 10; i++) {
172
+ savePost(`https://example.com/posts/${i}`, `Post ${i}`)
173
+ }
174
+ const posts = getPosts(5)
175
+ assert.strictEqual(posts.length, 5)
176
+ })
177
+ })
178
+
179
+ describe('Actor Cache', () => {
180
+ beforeEach(() => {
181
+ cleanup()
182
+ initStore(TEST_DB)
183
+ })
184
+
185
+ afterEach(() => {
186
+ const db = getStore()
187
+ db.close()
188
+ cleanup()
189
+ })
190
+
191
+ test('cacheActor and getCachedActor', () => {
192
+ const actor = {
193
+ id: 'https://example.com/user/alice',
194
+ type: 'Person',
195
+ preferredUsername: 'alice'
196
+ }
197
+ cacheActor(actor)
198
+ const cached = getCachedActor(actor.id)
199
+ assert.deepStrictEqual(cached, actor)
200
+ })
201
+
202
+ test('getCachedActor returns null for unknown', () => {
203
+ const cached = getCachedActor('https://example.com/unknown')
204
+ assert.strictEqual(cached, null)
205
+ })
206
+ })
207
+
208
+ describe('Activities', () => {
209
+ beforeEach(() => {
210
+ cleanup()
211
+ initStore(TEST_DB)
212
+ })
213
+
214
+ afterEach(() => {
215
+ const db = getStore()
216
+ db.close()
217
+ cleanup()
218
+ })
219
+
220
+ test('saveActivity and getActivities', () => {
221
+ const activity = {
222
+ id: 'https://example.com/activity/1',
223
+ type: 'Create',
224
+ actor: 'https://example.com/user/alice',
225
+ object: { type: 'Note', content: 'Hello' }
226
+ }
227
+ saveActivity(activity)
228
+ const activities = getActivities(10)
229
+ assert.strictEqual(activities.length, 1)
230
+ assert.strictEqual(activities[0].type, 'Create')
231
+ assert.deepStrictEqual(activities[0].raw, activity)
232
+ })
233
+ })