package service_test
import (
"testing"
"time"
"github.com/modelcontextprotocol/registry/internal/service"
)
func TestIsSemanticVersion(t *testing.T) {
tests := []struct {
name string
version string
want bool
}{
{"basic semver", "1.0.0", true},
{"with patch", "1.2.3", true},
{"with zeros", "0.0.0", true},
{"large numbers", "100.200.300", true},
{"with prerelease alpha", "1.0.0-alpha", true},
{"with prerelease beta", "2.1.3-beta", true},
{"with prerelease rc", "3.0.0-rc", true},
{"with prerelease number", "1.0.0-1", true},
{"with prerelease complex", "1.0.0-alpha.1", true},
{"with prerelease dots", "1.0.0-beta.2.3", true},
{"date format", "2021.11.15", true},
{"with hyphen in prerelease", "1.0.0-pre-release", true},
{"with v prefix", "v1.0.0", true},
{"empty string", "", false},
{"single number", "1", false},
{"two parts only", "1.0", false},
{"four parts", "1.0.0.0", false},
{"non-numeric major", "a.0.0", false},
{"non-numeric minor", "1.b.0", false},
{"non-numeric patch", "1.0.c", false},
{"empty prerelease", "1.0.0-", false},
{"special chars in prerelease", "1.0.0-alpha@1", false},
{"snapshot", "snapshot", false},
{"latest", "latest", false},
{"with leading zeros", "2021.03.05", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := service.IsSemanticVersion(tt.version); got != tt.want {
t.Errorf("IsSemanticVersion(%q) = %v, want %v", tt.version, got, tt.want)
}
})
}
}
func TestCompareSemanticVersions(t *testing.T) {
tests := []struct {
name string
version1 string
version2 string
want int
}{
{"major less", "1.0.0", "2.0.0", -1},
{"major greater", "2.0.0", "1.0.0", 1},
{"major equal", "1.0.0", "1.0.0", 0},
{"minor less", "1.1.0", "1.2.0", -1},
{"minor greater", "1.2.0", "1.1.0", 1},
{"minor equal", "1.2.0", "1.2.0", 0},
{"patch less", "1.0.1", "1.0.2", -1},
{"patch greater", "1.0.2", "1.0.1", 1},
{"patch equal", "1.0.1", "1.0.1", 0},
{"complex less", "1.9.9", "2.0.0", -1},
{"complex greater", "2.0.0", "1.9.9", 1},
{"complex mixed", "1.10.0", "1.2.0", 1},
{"prerelease vs stable less", "1.0.0-alpha", "1.0.0", -1},
{"stable vs prerelease greater", "1.0.0", "1.0.0-alpha", 1},
{"prerelease alpha vs beta", "1.0.0-alpha", "1.0.0-beta", -1},
{"prerelease beta vs alpha", "1.0.0-beta", "1.0.0-alpha", 1},
{"prerelease same", "1.0.0-alpha", "1.0.0-alpha", 0},
{"prerelease numeric", "1.0.0-1", "1.0.0-2", -1},
{"prerelease complex", "1.0.0-alpha.1", "1.0.0-alpha.2", -1},
{"prerelease rc vs alpha", "1.0.0-rc", "1.0.0-alpha", 1},
{"prerelease alpha.10 vs alpha.2", "1.0.0-alpha.10", "1.0.0-alpha.2", 1},
{"prerelease alpha.2 vs alpha.10", "1.0.0-alpha.2", "1.0.0-alpha.10", -1},
{"prerelease beta.100 vs beta.9", "1.0.0-beta.100", "1.0.0-beta.9", 1},
{"numeric vs alphanumeric precedence", "1.0.0-1", "1.0.0-alpha", -1},
{"alphanumeric vs numeric precedence", "1.0.0-alpha", "1.0.0-1", 1},
{"shorter prerelease list precedence", "1.0.0-alpha", "1.0.0-alpha.1", -1},
{"longer prerelease list precedence", "1.0.0-alpha.1", "1.0.0-alpha", 1},
{"mixed numeric and alpha", "1.0.0-alpha.1.beta", "1.0.0-alpha.1.2", 1},
{"complex prerelease ordering", "1.0.0-rc.1", "1.0.0-rc.10", -1},
{"zero versions", "0.0.0", "0.0.1", -1},
{"large numbers", "100.200.300", "100.200.301", -1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
now := time.Now()
if got := service.CompareVersions(tt.version1, tt.version2, now, now); got != tt.want {
t.Errorf("CompareVersions(%q, %q, %v, %v) = %v, want %v", tt.version1, tt.version2, now, now, got, tt.want)
}
})
}
}
func TestCompareVersions(t *testing.T) {
now := time.Now()
earlier := now.Add(-time.Hour)
later := now.Add(time.Hour)
tests := []struct {
name string
version1 string
version2 string
timestamp1 time.Time
timestamp2 time.Time
want int
}{
{"both semver less", "1.0.0", "2.0.0", now, now, -1},
{"both semver greater", "2.0.0", "1.0.0", now, now, 1},
{"both semver equal", "1.0.0", "1.0.0", now, now, 0},
{"both semver ignore timestamps", "1.0.0", "2.0.0", later, earlier, -1},
{"neither semver earlier", "snapshot", "latest", earlier, later, -1},
{"neither semver later", "snapshot", "latest", later, earlier, 1},
{"neither semver same time", "snapshot", "latest", now, now, 0},
{"neither semver v-prefix", "v2021.03.15", "v2021.03.16", earlier, later, -1},
{"semver vs non-semver", "1.0.0", "snapshot", now, now, 1},
{"non-semver vs semver", "snapshot", "1.0.0", now, now, -1},
{"semver vs snapshot", "2.0.0", "snapshot", earlier, later, 1},
{"latest vs semver", "latest", "1.0.0", later, earlier, -1},
{"semver prerelease vs non-semver", "1.0.0-alpha", "custom", now, now, 1},
{"empty vs semver", "", "1.0.0", now, now, -1},
{"semver vs empty", "1.0.0", "", now, now, 1},
{"both empty", "", "", now, now, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := service.CompareVersions(tt.version1, tt.version2, tt.timestamp1, tt.timestamp2); got != tt.want {
t.Errorf("CompareVersions(%q, %q, %v, %v) = %v, want %v",
tt.version1, tt.version2, tt.timestamp1, tt.timestamp2, got, tt.want)
}
})
}
}