mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import assert from 'node:assert'
2import {AddressInfo} from 'node:net'
3import {after, before, describe, it} from 'node:test'
4
5import {Database, envToCfg, LinkService, readEnv} from '../src/index.js'
6
7describe('link service', async () => {
8 let linkService: LinkService
9 let baseUrl: string
10 before(async () => {
11 const env = readEnv()
12 const cfg = envToCfg({
13 ...env,
14 hostnames: ['test.bsky.link'],
15 appHostname: 'test.bsky.app',
16 dbPostgresSchema: 'link_test',
17 dbPostgresUrl: process.env.DB_POSTGRES_URL,
18 })
19 const migrateDb = Database.postgres({
20 url: cfg.db.url,
21 schema: cfg.db.schema,
22 })
23 await migrateDb.migrateToLatestOrThrow()
24 await migrateDb.close()
25 linkService = await LinkService.create(cfg)
26 await linkService.start()
27 const {port} = linkService.server?.address() as AddressInfo
28 baseUrl = `http://localhost:${port}`
29 })
30
31 after(async () => {
32 await linkService?.destroy()
33 })
34
35 it('creates a starter pack link', async () => {
36 const link = await getLink('/start/did:example:alice/xxx')
37 const url = new URL(link)
38 assert.strictEqual(url.origin, 'https://test.bsky.link')
39 assert.match(url.pathname, /^\/[a-z0-9]+$/i)
40 })
41
42 it('normalizes input paths and provides same link each time.', async () => {
43 const link1 = await getLink('/start/did%3Aexample%3Abob/yyy')
44 const link2 = await getLink('/start/did:example:bob/yyy/')
45 assert.strictEqual(link1, link2)
46 })
47
48 it('serves permanent redirect, preserving query params.', async () => {
49 const link = await getLink('/start/did:example:carol/zzz/')
50 const [status, location] = await getRedirect(`${link}?a=b`)
51 assert.strictEqual(status, 301)
52 const locationUrl = new URL(location)
53 assert.strictEqual(
54 locationUrl.pathname + locationUrl.search,
55 '/start/did:example:carol/zzz?a=b',
56 )
57 })
58
59 it('returns json object with url when requested', async () => {
60 const link = await getLink('/start/did:example:carol/zzz/')
61 const [status, json] = await getJsonRedirect(link)
62 assert.strictEqual(status, 200)
63 assert(json.url)
64 const url = new URL(json.url)
65 assert.strictEqual(url.pathname, '/start/did:example:carol/zzz')
66 })
67
68 it('returns 404 for unknown link when requesting json', async () => {
69 const [status, json] = await getJsonRedirect(
70 'https://test.bsky.link/unknown',
71 )
72 assert(json.error)
73 assert(json.message)
74 assert.strictEqual(status, 404)
75 assert.strictEqual(json.error, 'NotFound')
76 assert.strictEqual(json.message, 'Link not found')
77 })
78
79 async function getRedirect(link: string): Promise<[number, string]> {
80 const url = new URL(link)
81 const base = new URL(baseUrl)
82 url.protocol = base.protocol
83 url.host = base.host
84 const res = await fetch(url, {redirect: 'manual'})
85 await res.arrayBuffer() // drain
86 assert(
87 res.status === 301 || res.status === 303,
88 'response was not a redirect',
89 )
90 return [res.status, res.headers.get('location') ?? '']
91 }
92
93 async function getJsonRedirect(
94 link: string,
95 ): Promise<[number, {url?: string; error?: string; message?: string}]> {
96 const url = new URL(link)
97 const base = new URL(baseUrl)
98 url.protocol = base.protocol
99 url.host = base.host
100 const res = await fetch(url, {
101 redirect: 'manual',
102 headers: {accept: 'application/json,text/html'},
103 })
104 assert(
105 res.headers.get('content-type')?.startsWith('application/json'),
106 'content type was not json',
107 )
108 const json = await res.json()
109 return [res.status, json]
110 }
111
112 async function getLink(path: string): Promise<string> {
113 const res = await fetch(new URL('/link', baseUrl), {
114 method: 'post',
115 headers: {'content-type': 'application/json'},
116 body: JSON.stringify({path}),
117 })
118 assert.strictEqual(res.status, 200)
119 const payload = await res.json()
120 assert(typeof payload.url === 'string')
121 return payload.url
122 }
123})