open source is social v-it.org
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 sol pbc
3
4import { describe, test, expect } from 'bun:test';
5import {
6 isValidSkillName,
7 isSkillRef,
8 isValidSkillRef,
9 skillRefFromName,
10 nameFromSkillRef,
11 SKILL_NAME_PATTERN,
12 SKILL_REF_PATTERN,
13 SKILL_NAME_MAX,
14} from '../src/lib/skill-ref.js';
15
16describe('isValidSkillName', () => {
17 test('accepts simple lowercase names', () => {
18 expect(isValidSkillName('vit')).toBe(true);
19 expect(isValidSkillName('agent-test-patterns')).toBe(true);
20 expect(isValidSkillName('pdf-form-filler')).toBe(true);
21 });
22
23 test('accepts names with numbers', () => {
24 expect(isValidSkillName('tool2')).toBe(true);
25 expect(isValidSkillName('agent-v2-helper')).toBe(true);
26 });
27
28 test('rejects names starting with hyphen', () => {
29 expect(isValidSkillName('-bad')).toBe(false);
30 });
31
32 test('rejects names with consecutive hyphens', () => {
33 expect(isValidSkillName('bad--name')).toBe(false);
34 });
35
36 test('rejects names starting with number', () => {
37 expect(isValidSkillName('3bad')).toBe(false);
38 });
39
40 test('rejects uppercase', () => {
41 expect(isValidSkillName('BadName')).toBe(false);
42 expect(isValidSkillName('ALLCAPS')).toBe(false);
43 });
44
45 test('rejects empty and null', () => {
46 expect(isValidSkillName('')).toBe(false);
47 expect(isValidSkillName(null)).toBe(false);
48 expect(isValidSkillName(undefined)).toBe(false);
49 });
50
51 test('rejects names over 64 chars', () => {
52 const long = 'a'.repeat(65);
53 expect(isValidSkillName(long)).toBe(false);
54 const justRight = 'a'.repeat(64);
55 expect(isValidSkillName(justRight)).toBe(true);
56 });
57
58 test('rejects names with special chars', () => {
59 expect(isValidSkillName('has_underscore')).toBe(false);
60 expect(isValidSkillName('has.dot')).toBe(false);
61 expect(isValidSkillName('has space')).toBe(false);
62 expect(isValidSkillName('has@sign')).toBe(false);
63 });
64
65 test('rejects trailing hyphen', () => {
66 expect(isValidSkillName('bad-')).toBe(false);
67 });
68});
69
70describe('isSkillRef', () => {
71 test('detects skill- prefix', () => {
72 expect(isSkillRef('skill-vit')).toBe(true);
73 expect(isSkillRef('skill-agent-test')).toBe(true);
74 });
75
76 test('rejects non-skill refs', () => {
77 expect(isSkillRef('fast-cache-invalidation')).toBe(false);
78 expect(isSkillRef('vit')).toBe(false);
79 expect(isSkillRef('')).toBe(false);
80 expect(isSkillRef(null)).toBe(false);
81 });
82});
83
84describe('isValidSkillRef', () => {
85 test('validates well-formed skill refs', () => {
86 expect(isValidSkillRef('skill-vit')).toBe(true);
87 expect(isValidSkillRef('skill-agent-test-patterns')).toBe(true);
88 expect(isValidSkillRef('skill-pdf2')).toBe(true);
89 });
90
91 test('rejects invalid skill refs', () => {
92 expect(isValidSkillRef('skill-')).toBe(false);
93 expect(isValidSkillRef('skill--bad')).toBe(false);
94 expect(isValidSkillRef('skill-Bad')).toBe(false);
95 expect(isValidSkillRef('not-a-skill-ref')).toBe(false);
96 expect(isValidSkillRef('')).toBe(false);
97 });
98});
99
100describe('skillRefFromName / nameFromSkillRef', () => {
101 test('round-trips correctly', () => {
102 expect(skillRefFromName('vit')).toBe('skill-vit');
103 expect(nameFromSkillRef('skill-vit')).toBe('vit');
104 expect(nameFromSkillRef(skillRefFromName('agent-test'))).toBe('agent-test');
105 });
106
107 test('nameFromSkillRef returns null for non-skill refs', () => {
108 expect(nameFromSkillRef('fast-cache-invalidation')).toBe(null);
109 expect(nameFromSkillRef('')).toBe(null);
110 expect(nameFromSkillRef(null)).toBe(null);
111 });
112});