Sifa professional network API (Fastify, AT Protocol, Jetstream)
sifa.id/
1import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2import { createDb } from '../../src/db/index.js';
3import { canonicalSkills, unresolvedSkills } from '../../src/db/schema/index.js';
4import { resolveSkill } from '../../src/services/skill-normalization.js';
5import { eq, sql } from 'drizzle-orm';
6
7describe('resolveSkill (integration)', () => {
8 const db = createDb(process.env.DATABASE_URL ?? 'postgresql://sifa:sifa@localhost:5432/sifa');
9
10 beforeAll(async () => {
11 await db
12 .insert(canonicalSkills)
13 .values({
14 canonicalName: 'JavaScript',
15 slug: 'javascript',
16 category: 'technical',
17 aliases: ['js', 'javascript', 'ecmascript', 'java script'],
18 userCount: 0,
19 })
20 .onConflictDoNothing();
21 });
22
23 afterAll(async () => {
24 await db.delete(canonicalSkills).where(eq(canonicalSkills.slug, 'javascript'));
25 await db.execute(
26 sql`DELETE FROM unresolved_skills WHERE normalized_name IN ('javascript', 'completely-unknown-skill-xyz')`,
27 );
28 await db.$client.end();
29 });
30
31 it('resolves an alias to canonical skill', async () => {
32 const result = await resolveSkill(db, 'JS');
33 expect(result).not.toBeNull();
34 expect(result?.canonicalName).toBe('JavaScript');
35 expect(result?.slug).toBe('javascript');
36 });
37
38 it('resolves exact canonical name', async () => {
39 const result = await resolveSkill(db, 'JavaScript');
40 expect(result).not.toBeNull();
41 expect(result?.canonicalName).toBe('JavaScript');
42 });
43
44 it('returns null and queues unresolved skill when no match found', async () => {
45 const result = await resolveSkill(db, 'completely-unknown-skill-xyz');
46 expect(result).toBeNull();
47
48 const unresolved = await db
49 .select()
50 .from(unresolvedSkills)
51 .where(eq(unresolvedSkills.normalizedName, 'completely-unknown-skill-xyz'));
52 expect(unresolved).toHaveLength(1);
53 expect(unresolved[0].occurrences).toBe(1);
54 });
55
56 it('increments occurrence count for repeated unresolved skills', async () => {
57 await resolveSkill(db, 'completely-unknown-skill-xyz');
58 const unresolved = await db
59 .select()
60 .from(unresolvedSkills)
61 .where(eq(unresolvedSkills.normalizedName, 'completely-unknown-skill-xyz'));
62 expect(unresolved[0].occurrences).toBe(2);
63 });
64});