Add a new nixpkgs-search CLI that combines NixOS options search with Nix package search functionality. This provides two MCP servers from a single binary: - `nixpkgs-search options serve` for NixOS options - `nixpkgs-search packages serve` for Nix packages Key changes: - Add packages table to database schema (version 3) - Add Package type and search methods to database layer - Create internal/packages/ with indexer and parser for nix-env JSON - Add MCP server mode (options/packages) with separate tool sets - Add package handlers: search_packages, get_package - Create cmd/nixpkgs-search with combined indexing support - Update flake.nix with nixpkgs-search package (now default) - Bump version to 0.2.0 The index command can index both options and packages together, or use --no-packages/--no-options flags for partial indexing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
83 lines
1.8 KiB
Go
83 lines
1.8 KiB
Go
package packages
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateRevision(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
revision string
|
|
expectErr bool
|
|
}{
|
|
{"valid hash", "abc123def456", false},
|
|
{"valid channel", "nixos-unstable", false},
|
|
{"valid version channel", "nixos-24.11", false},
|
|
{"empty", "", true},
|
|
{"too long", "a" + string(make([]byte, 100)), true},
|
|
{"shell injection", "$(rm -rf /)", true},
|
|
{"path traversal", "../../../etc/passwd", true},
|
|
{"semicolon", "abc;rm -rf /", true},
|
|
{"backtick", "`whoami`", true},
|
|
{"space", "abc def", true},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateRevision(tc.revision)
|
|
if tc.expectErr && err == nil {
|
|
t.Error("Expected error, got nil")
|
|
}
|
|
if !tc.expectErr && err != nil {
|
|
t.Errorf("Expected no error, got %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestResolveRevision(t *testing.T) {
|
|
idx := &Indexer{}
|
|
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
}{
|
|
{"nixos-unstable", "nixos-unstable"},
|
|
{"nixos-stable", "nixos-24.11"},
|
|
{"nixos-24.11", "nixos-24.11"},
|
|
{"abc123", "abc123"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.input, func(t *testing.T) {
|
|
result := idx.ResolveRevision(tc.input)
|
|
if result != tc.expected {
|
|
t.Errorf("Expected %q, got %q", tc.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetChannelName(t *testing.T) {
|
|
idx := &Indexer{}
|
|
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
}{
|
|
{"nixos-unstable", "nixos-unstable"},
|
|
{"nixos-stable", "nixos-stable"},
|
|
{"nixos-24.11", "nixos-24.11"},
|
|
{"abc123", ""},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.input, func(t *testing.T) {
|
|
result := idx.GetChannelName(tc.input)
|
|
if result != tc.expected {
|
|
t.Errorf("Expected %q, got %q", tc.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|