Skip to content

Commit 0542a64

Browse files
committed
Add test for the detector code in Social/Mastodon
1 parent 27f4709 commit 0542a64

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
2+
import { getSoftwareName, getUrlDomain, isMastodonInstance, normalizeURL } from '../detector'
3+
4+
const createFetchResponse = <T>(data: T) => ({
5+
json: vi.fn().mockResolvedValue(data),
6+
}) as unknown as Response
7+
8+
describe('mastodon detector utilities', () => {
9+
const fetchMock = vi.fn<typeof fetch>()
10+
const schemaRel = 'http://nodeinfo.diaspora.software/ns/schema/2.1'
11+
12+
beforeEach(() => {
13+
fetchMock.mockReset()
14+
vi.stubGlobal('fetch', fetchMock)
15+
})
16+
17+
afterEach(() => {
18+
vi.unstubAllGlobals()
19+
vi.restoreAllMocks()
20+
})
21+
22+
test('normalizeURL enforces scheme and trailing slash', () => {
23+
expect(normalizeURL('mastodon.social')).toBe('https://mastodon.social/')
24+
expect(normalizeURL('http://example.com/path')).toBe('http://example.com/path/')
25+
})
26+
27+
test('getUrlDomain normalizes plain hostnames', () => {
28+
expect(getUrlDomain('mastodon.social')).toBe('mastodon.social')
29+
expect(getUrlDomain('https://fediverse.example/foo')).toBe('fediverse.example')
30+
})
31+
32+
test('getSoftwareName fetches nodeinfo list from well-known path', async () => {
33+
fetchMock
34+
.mockResolvedValueOnce(
35+
createFetchResponse({
36+
links: [
37+
{ rel: schemaRel, href: 'https://mastodon.social/nodeinfo/2.1' },
38+
{ rel: 'other', href: 'https://mastodon.social/nodeinfo/1.0' },
39+
],
40+
})
41+
)
42+
.mockResolvedValueOnce(
43+
createFetchResponse({
44+
software: { name: 'Mastodon', version: '4.2.0' },
45+
})
46+
)
47+
48+
const software = await getSoftwareName('mastodon.social')
49+
50+
expect(software).toBe('Mastodon')
51+
expect(fetchMock).toHaveBeenCalledTimes(2)
52+
53+
const firstCallUrl = fetchMock.mock.calls[0]?.[0] as URL
54+
expect(firstCallUrl.pathname).toBe('/.well-known/nodeinfo')
55+
expect(firstCallUrl.origin).toBe('https://mastodon.social')
56+
})
57+
58+
test('getSoftwareName returns undefined when nodeinfo cannot be fetched', async () => {
59+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
60+
fetchMock.mockRejectedValueOnce(new Error('network failure'))
61+
62+
await expect(getSoftwareName('mastodon.social')).resolves.toBeUndefined()
63+
expect(errorSpy).toHaveBeenCalled()
64+
})
65+
66+
test('isMastodonInstance returns true for supported flavours', async () => {
67+
fetchMock
68+
.mockResolvedValueOnce(
69+
createFetchResponse({
70+
links: [{ rel: schemaRel, href: 'https://example.social/nodeinfo' }],
71+
})
72+
)
73+
.mockResolvedValueOnce(
74+
createFetchResponse({
75+
software: { name: 'Hometown', version: '1.0' },
76+
})
77+
)
78+
79+
await expect(isMastodonInstance('example.social')).resolves.toBe(true)
80+
})
81+
82+
test('isMastodonInstance returns false when NodeInfo is missing', async () => {
83+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
84+
fetchMock.mockResolvedValueOnce(
85+
createFetchResponse({
86+
links: [],
87+
})
88+
)
89+
90+
await expect(isMastodonInstance('pleroma.example')).resolves.toBe(false)
91+
expect(warnSpy).toHaveBeenCalledWith('No NodeInfo found for domain:', 'pleroma.example')
92+
})
93+
})

0 commit comments

Comments
 (0)