test: add comprehensive WebFinger tests (17 tests)
Added complete test coverage for WebFinger JRD generation: ## Static Resources Tests (11 tests) - Unknown resource returns null - Basic JRD generation - Subject defaults to resource when not provided - Aliases support - Properties support (URI-based names) - Links support with rel, href, type - Rel filtering (single and multiple) - Link titles with language tags - Complete JRD with all fields ## Protocol Support Tests - HTTP/HTTPS resource URIs - ActivityPub profile discovery (self rel, application/activity+json) - OpenID Connect issuer discovery ## Edge Cases (4 tests) - Resource with no links - Empty aliases array omitted - Empty properties object omitted - All links filtered out by rel Test suite now at 89 tests (up from 72), all passing. Coverage includes RFC 7033 compliance, common use cases (Mastodon/ActivityPub, OpenID), and edge cases.
This commit is contained in:
parent
a485092767
commit
25ad52e68b
480
tests/webfinger.test.ts
Normal file
480
tests/webfinger.test.ts
Normal file
@ -0,0 +1,480 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { generateWebFingerJRD } from '../src/generators/webfinger.js';
|
||||
import type { WebFingerConfig } from '../src/types.js';
|
||||
|
||||
describe('generateWebFingerJRD', () => {
|
||||
const testURL = new URL('https://example.com');
|
||||
|
||||
describe('Static Resources', () => {
|
||||
it('returns null for unknown resource', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
subject: 'acct:alice@example.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:bob@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('generates basic JRD for known resource', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
subject: 'acct:alice@example.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.subject).toBe('acct:alice@example.com');
|
||||
});
|
||||
|
||||
it('uses resource as subject when subject not provided', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.subject).toBe('acct:alice@example.com');
|
||||
});
|
||||
|
||||
it('includes aliases when provided', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
aliases: [
|
||||
'https://example.com/~alice',
|
||||
'https://example.com/users/alice',
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.aliases).toEqual([
|
||||
'https://example.com/~alice',
|
||||
'https://example.com/users/alice',
|
||||
]);
|
||||
});
|
||||
|
||||
it('includes properties when provided', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
properties: {
|
||||
'http://schema.org/name': 'Alice Smith',
|
||||
'http://example.com/role': 'Developer',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.properties).toEqual({
|
||||
'http://schema.org/name': 'Alice Smith',
|
||||
'http://example.com/role': 'Developer',
|
||||
});
|
||||
});
|
||||
|
||||
it('includes links when provided', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: 'https://example.com/~alice',
|
||||
type: 'text/html',
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/avatar',
|
||||
href: 'https://example.com/avatars/alice.jpg',
|
||||
type: 'image/jpeg',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toHaveLength(2);
|
||||
expect(jrd.links[0].rel).toBe('http://webfinger.net/rel/profile-page');
|
||||
expect(jrd.links[0].href).toBe('https://example.com/~alice');
|
||||
expect(jrd.links[1].rel).toBe('http://webfinger.net/rel/avatar');
|
||||
});
|
||||
|
||||
it('filters links by rel when requested', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: 'https://example.com/~alice',
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/avatar',
|
||||
href: 'https://example.com/avatars/alice.jpg',
|
||||
},
|
||||
{
|
||||
rel: 'http://openid.net/specs/connect/1.0/issuer',
|
||||
href: 'https://auth.example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
['http://webfinger.net/rel/profile-page'],
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toHaveLength(1);
|
||||
expect(jrd.links[0].rel).toBe('http://webfinger.net/rel/profile-page');
|
||||
});
|
||||
|
||||
it('filters links by multiple rels', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{ rel: 'profile', href: 'https://example.com/~alice' },
|
||||
{ rel: 'avatar', href: 'https://example.com/avatars/alice.jpg' },
|
||||
{ rel: 'blog', href: 'https://blog.example.com/alice' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
['profile', 'blog'],
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toHaveLength(2);
|
||||
expect(jrd.links[0].rel).toBe('profile');
|
||||
expect(jrd.links[1].rel).toBe('blog');
|
||||
});
|
||||
|
||||
it('includes link titles when provided', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: 'https://example.com/~alice',
|
||||
titles: {
|
||||
en: 'Alice Smith Profile',
|
||||
es: 'Perfil de Alice Smith',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links[0].titles).toEqual({
|
||||
en: 'Alice Smith Profile',
|
||||
es: 'Perfil de Alice Smith',
|
||||
});
|
||||
});
|
||||
|
||||
it('generates complete JRD with all fields', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
subject: 'acct:alice@example.com',
|
||||
aliases: ['https://example.com/~alice'],
|
||||
properties: {
|
||||
'http://schema.org/name': 'Alice Smith',
|
||||
},
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: 'https://example.com/~alice',
|
||||
type: 'text/html',
|
||||
titles: { en: 'Profile' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.subject).toBe('acct:alice@example.com');
|
||||
expect(jrd.aliases).toEqual(['https://example.com/~alice']);
|
||||
expect(jrd.properties).toEqual({
|
||||
'http://schema.org/name': 'Alice Smith',
|
||||
});
|
||||
expect(jrd.links).toHaveLength(1);
|
||||
expect(jrd.links[0].rel).toBe('http://webfinger.net/rel/profile-page');
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTTP Resource URIs', () => {
|
||||
it('supports https:// resource URIs', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'https://example.com/user/alice',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
href: 'https://example.com/user/alice',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'https://example.com/user/alice',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.subject).toBe('https://example.com/user/alice');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ActivityPub / Fediverse', () => {
|
||||
it('supports ActivityPub profile discovery', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
aliases: ['https://example.com/@alice'],
|
||||
links: [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: 'https://example.com/users/alice',
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: 'https://example.com/@alice',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toHaveLength(2);
|
||||
expect(jrd.links[0].type).toBe('application/activity+json');
|
||||
expect(jrd.links[0].rel).toBe('self');
|
||||
});
|
||||
});
|
||||
|
||||
describe('OpenID Connect', () => {
|
||||
it('supports OpenID Connect issuer discovery', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{
|
||||
rel: 'http://openid.net/specs/connect/1.0/issuer',
|
||||
href: 'https://auth.example.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
['http://openid.net/specs/connect/1.0/issuer'],
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toHaveLength(1);
|
||||
expect(jrd.links[0].rel).toBe(
|
||||
'http://openid.net/specs/connect/1.0/issuer'
|
||||
);
|
||||
expect(jrd.links[0].href).toBe('https://auth.example.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('returns valid JSON for resource with no links', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.subject).toBe('acct:alice@example.com');
|
||||
expect(jrd.links).toBeUndefined();
|
||||
});
|
||||
|
||||
it('omits empty aliases array', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
aliases: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.aliases).toBeUndefined();
|
||||
});
|
||||
|
||||
it('omits empty properties object', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
properties: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
undefined,
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.properties).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns null when all links filtered out by rel', async () => {
|
||||
const config: WebFingerConfig = {
|
||||
resources: [
|
||||
{
|
||||
resource: 'acct:alice@example.com',
|
||||
links: [
|
||||
{ rel: 'profile', href: 'https://example.com/~alice' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await generateWebFingerJRD(
|
||||
config,
|
||||
'acct:alice@example.com',
|
||||
['avatar'], // Requesting rel that doesn't exist
|
||||
testURL
|
||||
);
|
||||
|
||||
const jrd = JSON.parse(result!);
|
||||
expect(jrd.links).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user