Exosphere is a set of small, modular, self-hostable community tools built on the AT Protocol. app.exosphere.site
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: fix unit and e2e tests

Hugo 589754b1 6f948d90

+67 -58
+38 -20
packages/app/e2e/seed.ts
··· 67 67 [SPHERE_ID], 68 68 ); 69 69 70 + // IDs must be valid TID strings (tidToDate derives createdAt from them) 71 + const FR_001 = "3mhyagkx2ok2m"; 72 + const FR_002 = "3mhyagkx2ol2m"; 73 + const FR_003 = "3mhyagkx2om2m"; 74 + const FR_004 = "3mhyagkx2on2m"; 75 + const FR_005 = "3mhyagkx2oo2m"; 76 + const COMMENT_001 = "3mhyagkx2op2m"; 77 + const COMMENT_002 = "3mhyagkx2oq2m"; 78 + 70 79 const featureRequests = [ 71 80 { 72 - id: "fr-001", 81 + id: FR_001, 73 82 number: 1, 74 83 title: "Add dark mode support", 75 84 description: "It would be great to have a dark mode option for better readability at night.", ··· 78 87 authorDid: MEMBER_DID, 79 88 }, 80 89 { 81 - id: "fr-002", 90 + id: FR_002, 82 91 number: 2, 83 92 title: "Export data as CSV", 84 93 description: "Allow users to export their data in CSV format for analysis.", ··· 87 96 authorDid: OWNER_DID, 88 97 }, 89 98 { 90 - id: "fr-003", 99 + id: FR_003, 91 100 number: 3, 92 101 title: "Mobile app", 93 102 description: "A native mobile application would improve the user experience significantly.", ··· 96 105 authorDid: MEMBER_DID, 97 106 }, 98 107 { 99 - id: "fr-004", 108 + id: FR_004, 100 109 number: 4, 101 110 title: "Keyboard shortcuts", 102 111 description: "Add keyboard shortcuts for common actions like voting and navigation.", ··· 105 114 authorDid: OWNER_DID, 106 115 }, 107 116 { 108 - id: "fr-005", 117 + id: FR_005, 109 118 number: 5, 110 119 title: "Windows phone support", 111 120 description: "Please add support for Windows Phone platform.", ··· 124 133 } 125 134 126 135 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 127 - "fr-001", 136 + FR_001, 128 137 OWNER_DID, 129 138 ]); 130 139 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 131 - "fr-001", 140 + FR_001, 132 141 MEMBER_DID, 133 142 ]); 134 143 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 135 - "fr-002", 144 + FR_002, 136 145 MEMBER_DID, 137 146 ]); 138 147 139 148 db.run( 140 149 `INSERT INTO feature_request_comments (id, request_id, author_did, content) VALUES (?, ?, ?, ?)`, 141 - ["comment-001", "fr-001", OWNER_DID, "Great idea! We should prioritize this."], 150 + [COMMENT_001, FR_001, OWNER_DID, "Great idea! We should prioritize this."], 142 151 ); 143 152 db.run( 144 153 `INSERT INTO feature_request_comments (id, request_id, author_did, content) VALUES (?, ?, ?, ?)`, 145 - ["comment-002", "fr-001", MEMBER_DID, "I agree, dark mode would be very useful."], 154 + [COMMENT_002, FR_001, MEMBER_DID, "I agree, dark mode would be very useful."], 146 155 ); 147 156 148 157 db.close(); ··· 170 179 "sphere-alpha", 171 180 ]); 172 181 182 + // IDs must be valid TID strings (tidToDate derives createdAt from them) 183 + const ALPHA_FR_001 = "3mhyagkx2or2m"; 184 + const ALPHA_FR_002 = "3mhyagkx2os2m"; 185 + const ALPHA_FR_003 = "3mhyagkx2ot2m"; 186 + const ALPHA_FR_004 = "3mhyagkx2ou2m"; 187 + const ALPHA_COMMENT_001 = "3mhyagkx2ov2m"; 188 + const BETA_FR_001 = "3mhyagkx2ow2m"; 189 + const BETA_FR_002 = "3mhyagkx2ox2m"; 190 + 173 191 // Alpha feature requests (#1-#4 — per-sphere numbering, covers all status tabs) 174 192 const alphaRequests = [ 175 193 { 176 - id: "alpha-fr-001", 194 + id: ALPHA_FR_001, 177 195 number: 1, 178 196 title: "Alpha dark mode", 179 197 description: "Dark mode for Alpha sphere.", ··· 182 200 authorDid: OWNER_DID, 183 201 }, 184 202 { 185 - id: "alpha-fr-002", 203 + id: ALPHA_FR_002, 186 204 number: 2, 187 205 title: "Alpha CSV export", 188 206 description: "CSV export for Alpha sphere.", ··· 191 209 authorDid: MEMBER_DID, 192 210 }, 193 211 { 194 - id: "alpha-fr-003", 212 + id: ALPHA_FR_003, 195 213 number: 3, 196 214 title: "Alpha done feature", 197 215 description: "A completed feature.", ··· 200 218 authorDid: OWNER_DID, 201 219 }, 202 220 { 203 - id: "alpha-fr-004", 221 + id: ALPHA_FR_004, 204 222 number: 4, 205 223 title: "Alpha rejected idea", 206 224 description: "A rejected feature.", ··· 228 246 } 229 247 230 248 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 231 - "alpha-fr-001", 249 + ALPHA_FR_001, 232 250 OWNER_DID, 233 251 ]); 234 252 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 235 - "alpha-fr-001", 253 + ALPHA_FR_001, 236 254 MEMBER_DID, 237 255 ]); 238 256 239 257 db.run( 240 258 `INSERT INTO feature_request_comments (id, request_id, author_did, content) VALUES (?, ?, ?, ?)`, 241 - ["alpha-comment-001", "alpha-fr-001", OWNER_DID, "Alpha sphere comment on dark mode."], 259 + [ALPHA_COMMENT_001, ALPHA_FR_001, OWNER_DID, "Alpha sphere comment on dark mode."], 242 260 ); 243 261 244 262 // ---- Sphere B: beta.test ---- ··· 259 277 // Beta feature requests (#1-#2 — independent numbering from Alpha) 260 278 const betaRequests = [ 261 279 { 262 - id: "beta-fr-001", 280 + id: BETA_FR_001, 263 281 number: 1, 264 282 title: "Beta mobile app", 265 283 description: "Mobile app for Beta sphere.", ··· 268 286 authorDid: MEMBER_DID, 269 287 }, 270 288 { 271 - id: "beta-fr-002", 289 + id: BETA_FR_002, 272 290 number: 2, 273 291 title: "Beta API access", 274 292 description: "Public API for Beta sphere.", ··· 296 314 } 297 315 298 316 db.run(`INSERT INTO feature_request_votes (request_id, author_did) VALUES (?, ?)`, [ 299 - "beta-fr-001", 317 + BETA_FR_001, 300 318 MEMBER_DID, 301 319 ]); 302 320
+3 -14
packages/core/src/__tests__/sphere-operations.test.ts
··· 80 80 // ---- upsertSphereFromRecord ---- 81 81 82 82 describe("upsertSphereFromRecord", () => { 83 - it("creates a new sphere and owner membership", () => { 83 + it("ignores record when no local sphere exists for the DID", () => { 84 84 upsertSphereFromRecord({ 85 85 did: OWNER_DID, 86 86 rkey: SPHERE_ID, ··· 92 92 pdsUri: "at://did:plc:owner1/com.exosphere.sphere/sphere-1", 93 93 }); 94 94 95 + // Spheres are created locally, not via Jetstream — upsert is a no-op 95 96 const sphere = db.select().from(spheres).where(eq(spheres.id, SPHERE_ID)).get(); 96 - expect(sphere).toBeDefined(); 97 - expect(sphere!.handle).toBe(SPHERE_HANDLE); 98 - expect(sphere!.name).toBe("My Sphere"); 99 - expect(sphere!.ownerDid).toBe(OWNER_DID); 100 - 101 - const member = db 102 - .select() 103 - .from(sphereMembers) 104 - .where(and(eq(sphereMembers.sphereId, SPHERE_ID), eq(sphereMembers.did, OWNER_DID))) 105 - .get(); 106 - expect(member).toBeDefined(); 107 - expect(member!.role).toBe("owner"); 108 - expect(member!.status).toBe("active"); 97 + expect(sphere).toBeUndefined(); 109 98 }); 110 99 111 100 it("updates an existing sphere owned by the same DID", () => {
+26 -24
packages/feature-requests/src/__tests__/db-operations.test.ts
··· 40 40 const AUTHOR_DID = "did:plc:author1"; 41 41 const MOD_DID = "did:plc:mod1"; 42 42 const SPHERE_ID = "test-sphere-001"; 43 + // Valid TID string for comment IDs (insertComment derives updatedAt via tidToDate) 44 + const COMMENT_TID_1 = "3mhy7w6tbg22b"; 43 45 44 46 function seedFR(overrides: Partial<typeof featureRequests.$inferInsert> & { id: string }) { 45 47 const values = { ··· 175 177 it("inserts a comment", () => { 176 178 seedFR({ id: "fr-1" }); 177 179 insertComment({ 178 - id: "c-1", 180 + id: COMMENT_TID_1, 179 181 requestId: "fr-1", 180 182 authorDid: AUTHOR_DID, 181 183 content: "Great idea!", ··· 190 192 it("upserts on conflict (same id updates content)", () => { 191 193 seedFR({ id: "fr-1" }); 192 194 insertComment({ 193 - id: "c-1", 195 + id: COMMENT_TID_1, 194 196 requestId: "fr-1", 195 197 authorDid: AUTHOR_DID, 196 198 content: "Original", 197 199 pdsUri: null, 198 200 }); 199 201 insertComment({ 200 - id: "c-1", 202 + id: COMMENT_TID_1, 201 203 requestId: "fr-1", 202 204 authorDid: AUTHOR_DID, 203 205 content: "Updated via upsert", ··· 207 209 const comment = db 208 210 .select() 209 211 .from(featureRequestComments) 210 - .where(eq(featureRequestComments.id, "c-1")) 212 + .where(eq(featureRequestComments.id, COMMENT_TID_1)) 211 213 .get(); 212 214 expect(comment!.content).toBe("Updated via upsert"); 213 215 }); ··· 215 217 it("updates a comment's content", () => { 216 218 seedFR({ id: "fr-1" }); 217 219 insertComment({ 218 - id: "c-1", 220 + id: COMMENT_TID_1, 219 221 requestId: "fr-1", 220 222 authorDid: AUTHOR_DID, 221 223 content: "Original", 222 224 pdsUri: null, 223 225 }); 224 - updateComment("c-1", "Edited content"); 226 + updateComment(COMMENT_TID_1, "Edited content"); 225 227 226 228 const comment = db 227 229 .select() 228 230 .from(featureRequestComments) 229 - .where(eq(featureRequestComments.id, "c-1")) 231 + .where(eq(featureRequestComments.id, COMMENT_TID_1)) 230 232 .get(); 231 233 expect(comment!.content).toBe("Edited content"); 232 234 }); ··· 234 236 it("cascade-deletes comment and its votes", () => { 235 237 seedFR({ id: "fr-1" }); 236 238 insertComment({ 237 - id: "c-1", 239 + id: COMMENT_TID_1, 238 240 requestId: "fr-1", 239 241 authorDid: AUTHOR_DID, 240 242 content: "Comment", 241 243 pdsUri: null, 242 244 }); 243 245 db.insert(featureRequestCommentVotes) 244 - .values({ commentId: "c-1", authorDid: "did:plc:voter1" }) 246 + .values({ commentId: COMMENT_TID_1, authorDid: "did:plc:voter1" }) 245 247 .run(); 246 248 247 - deleteCommentCascade("c-1"); 249 + deleteCommentCascade(COMMENT_TID_1); 248 250 249 251 expect(db.select().from(featureRequestComments).all()).toHaveLength(0); 250 252 expect(db.select().from(featureRequestCommentVotes).all()).toHaveLength(0); ··· 257 259 it("inserts and deletes a comment vote", () => { 258 260 seedFR({ id: "fr-1" }); 259 261 insertComment({ 260 - id: "c-1", 262 + id: COMMENT_TID_1, 261 263 requestId: "fr-1", 262 264 authorDid: AUTHOR_DID, 263 265 content: "Comment", 264 266 pdsUri: null, 265 267 }); 266 268 267 - insertCommentVote("c-1", "did:plc:voter1", null); 269 + insertCommentVote(COMMENT_TID_1, "did:plc:voter1", null); 268 270 expect(db.select().from(featureRequestCommentVotes).all()).toHaveLength(1); 269 271 270 - deleteCommentVoteByAuthor("c-1", "did:plc:voter1"); 272 + deleteCommentVoteByAuthor(COMMENT_TID_1, "did:plc:voter1"); 271 273 expect(db.select().from(featureRequestCommentVotes).all()).toHaveLength(0); 272 274 }); 273 275 274 276 it("ignores duplicate comment votes", () => { 275 277 seedFR({ id: "fr-1" }); 276 278 insertComment({ 277 - id: "c-1", 279 + id: COMMENT_TID_1, 278 280 requestId: "fr-1", 279 281 authorDid: AUTHOR_DID, 280 282 content: "Comment", 281 283 pdsUri: null, 282 284 }); 283 285 284 - insertCommentVote("c-1", "did:plc:voter1", null); 285 - insertCommentVote("c-1", "did:plc:voter1", null); 286 + insertCommentVote(COMMENT_TID_1, "did:plc:voter1", null); 287 + insertCommentVote(COMMENT_TID_1, "did:plc:voter1", null); 286 288 expect(db.select().from(featureRequestCommentVotes).all()).toHaveLength(1); 287 289 }); 288 290 }); ··· 379 381 it("hides and unhides a comment", () => { 380 382 seedFR({ id: "fr-1" }); 381 383 insertComment({ 382 - id: "c-1", 384 + id: COMMENT_TID_1, 383 385 requestId: "fr-1", 384 386 authorDid: AUTHOR_DID, 385 387 content: "Comment", 386 388 pdsUri: null, 387 389 }); 388 390 389 - hideComment("c-1", MOD_DID); 391 + hideComment(COMMENT_TID_1, MOD_DID); 390 392 let comment = db 391 393 .select() 392 394 .from(featureRequestComments) 393 - .where(eq(featureRequestComments.id, "c-1")) 395 + .where(eq(featureRequestComments.id, COMMENT_TID_1)) 394 396 .get(); 395 397 expect(comment!.hiddenAt).toBeTruthy(); 396 398 expect(comment!.moderatedBy).toBe(MOD_DID); 397 399 398 - unhideComment("c-1"); 400 + unhideComment(COMMENT_TID_1); 399 401 comment = db 400 402 .select() 401 403 .from(featureRequestComments) 402 - .where(eq(featureRequestComments.id, "c-1")) 404 + .where(eq(featureRequestComments.id, COMMENT_TID_1)) 403 405 .get(); 404 406 expect(comment!.hiddenAt).toBeNull(); 405 407 expect(comment!.moderatedBy).toBeNull(); ··· 420 422 421 423 it("hides a comment by pdsUri and returns true", () => { 422 424 seedFR({ id: "fr-1" }); 423 - const commentPdsUri = "at://did:plc:author1/com.exosphere.featureRequestComment/c-1"; 425 + const commentPdsUri = `at://did:plc:author1/com.exosphere.featureRequestComment/${COMMENT_TID_1}`; 424 426 insertComment({ 425 - id: "c-1", 427 + id: COMMENT_TID_1, 426 428 requestId: "fr-1", 427 429 authorDid: AUTHOR_DID, 428 430 content: "Comment", ··· 435 437 const comment = db 436 438 .select() 437 439 .from(featureRequestComments) 438 - .where(eq(featureRequestComments.id, "c-1")) 440 + .where(eq(featureRequestComments.id, COMMENT_TID_1)) 439 441 .get(); 440 442 expect(comment!.hiddenAt).toBeTruthy(); 441 443 });