Skip to main content
Glama

Keeper Secrets Manager - MCP

notation_test.go11.8 kB
package ksm import ( "testing" "github.com/keeper-security/ksm-mcp/pkg/types" ) func TestParseNotation(t *testing.T) { tests := []struct { name string notation string want *types.NotationResult wantErr bool }{ { name: "UID with field", notation: "NJ_xXSkk3xYI1h9ql5lAiQ/field/password", want: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "password", Index: -1, }, wantErr: false, }, { name: "UID with array field", notation: "NJ_xXSkk3xYI1h9ql5lAiQ/field/url[0]", want: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "url", Index: 0, }, wantErr: false, }, { name: "UID with custom field property", notation: "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/name[first]", want: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "name", Custom: true, Property: "first", Index: -1, }, wantErr: false, }, { name: "UID with nested array property", notation: "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/phone[0][number]", want: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "phone", Custom: true, Index: 0, Property: "number", }, wantErr: false, }, { name: "UID with file", notation: "NJ_xXSkk3xYI1h9ql5lAiQ/file/document.pdf", want: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", File: "document.pdf", Index: -1, }, wantErr: false, }, { name: "Title with field", notation: "My Secret/field/password", want: &types.NotationResult{ Title: "My Secret", Field: "password", Index: -1, }, wantErr: false, }, { name: "empty notation", notation: "", want: nil, wantErr: true, }, { name: "invalid format - no slash", notation: "UID123", want: nil, wantErr: true, }, { name: "invalid format - one part", notation: "UID123/", want: nil, wantErr: true, }, { name: "missing field name", notation: "UID123/field/", want: nil, wantErr: true, }, { name: "invalid field type", notation: "UID123/invalid/password", want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseNotation(tt.notation) if (err != nil) != tt.wantErr { t.Errorf("ParseNotation() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { if got.UID != tt.want.UID { t.Errorf("ParseNotation() UID = %v, want %v", got.UID, tt.want.UID) } if got.Title != tt.want.Title { t.Errorf("ParseNotation() Title = %v, want %v", got.Title, tt.want.Title) } if got.Field != tt.want.Field { t.Errorf("ParseNotation() Field = %v, want %v", got.Field, tt.want.Field) } if got.Custom != tt.want.Custom { t.Errorf("ParseNotation() Custom = %v, want %v", got.Custom, tt.want.Custom) } if got.Index != tt.want.Index { t.Errorf("ParseNotation() Index = %v, want %v", got.Index, tt.want.Index) } if got.Property != tt.want.Property { t.Errorf("ParseNotation() Property = %v, want %v", got.Property, tt.want.Property) } if got.File != tt.want.File { t.Errorf("ParseNotation() File = %v, want %v", got.File, tt.want.File) } } }) } } func TestBuildNotation(t *testing.T) { tests := []struct { name string result *types.NotationResult want string }{ { name: "UID with field", result: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "password", Index: -1, }, want: "NJ_xXSkk3xYI1h9ql5lAiQ/field/password", }, { name: "UID with array field", result: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "url", Index: 0, }, want: "NJ_xXSkk3xYI1h9ql5lAiQ/field/url[0]", }, { name: "UID with custom field property", result: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "name", Custom: true, Property: "first", Index: -1, }, want: "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/name[first]", }, { name: "UID with nested array property", result: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", Field: "phone", Custom: true, Index: 0, Property: "number", }, want: "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/phone[0][number]", }, { name: "UID with file", result: &types.NotationResult{ UID: "NJ_xXSkk3xYI1h9ql5lAiQ", File: "document.pdf", Index: -1, }, want: "NJ_xXSkk3xYI1h9ql5lAiQ/file/document.pdf", }, { name: "Title with field", result: &types.NotationResult{ Title: "My Secret", Field: "password", Index: -1, }, want: "My Secret/field/password", }, { name: "empty result", result: &types.NotationResult{}, want: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := BuildNotation(tt.result) if got != tt.want { t.Errorf("BuildNotation() = %v, want %v", got, tt.want) } }) } } func TestIsValidUID(t *testing.T) { tests := []struct { name string uid string valid bool }{ {"valid 16 chars", "1234567890123456", true}, {"valid 32 chars", "12345678901234567890123456789012", true}, {"valid with underscore", "NJ_xXSkk3xYI1h9ql5lAiQ", true}, {"valid with hyphen", "abc-def-123-456-789", true}, {"too short", "123456789012345", false}, {"too long", "123456789012345678901234567890123", false}, {"with space", "12345678901234 56", false}, {"with special char", "1234567890123456!", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := isValidUID(tt.uid) if got != tt.valid { t.Errorf("isValidUID(%s) = %v, want %v", tt.uid, got, tt.valid) } }) } } func TestExtractFieldPath(t *testing.T) { tests := []struct { name string notation string want string wantErr bool }{ { name: "simple field", notation: "UID123/field/password", want: "field/password", wantErr: false, }, { name: "custom field", notation: "UID123/custom_field/apiKey", want: "custom_field/apiKey", wantErr: false, }, { name: "array field", notation: "UID123/field/url[0]", want: "field/url[0]", wantErr: false, }, { name: "property field", notation: "UID123/custom_field/name[first]", want: "custom_field/name[first]", wantErr: false, }, { name: "nested field", notation: "UID123/custom_field/phone[0][number]", want: "custom_field/phone[0][number]", wantErr: false, }, { name: "invalid notation", notation: "invalid", want: "", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ExtractFieldPath(tt.notation) if (err != nil) != tt.wantErr { t.Errorf("ExtractFieldPath() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("ExtractFieldPath() = %v, want %v", got, tt.want) } }) } } func TestIsFileNotation(t *testing.T) { tests := []struct { name string notation string want bool }{ {"file notation", "UID123/file/document.pdf", true}, {"field notation", "UID123/field/password", false}, {"custom field notation", "UID123/custom_field/apiKey", false}, {"invalid notation", "invalid", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := IsFileNotation(tt.notation) if got != tt.want { t.Errorf("IsFileNotation(%s) = %v, want %v", tt.notation, got, tt.want) } }) } } func TestIsCustomFieldNotation(t *testing.T) { tests := []struct { name string notation string want bool }{ {"custom field notation", "UID123/custom_field/apiKey", true}, {"field notation", "UID123/field/password", false}, {"file notation", "UID123/file/document.pdf", false}, {"invalid notation", "invalid", false}, {"empty", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := IsCustomFieldNotation(tt.notation) if got != tt.want { t.Errorf("IsCustomFieldNotation(%s) = %v, want %v", tt.notation, got, tt.want) } }) } } func TestSplitNotationParts(t *testing.T) { tests := []struct { name string notation string wantRef string wantType string wantPath string wantErr bool }{ { name: "simple field", notation: "UID123/field/password", wantRef: "UID123", wantType: "field", wantPath: "password", wantErr: false, }, { name: "custom field with array", notation: "UID123/custom_field/phones[0]", wantRef: "UID123", wantType: "custom_field", wantPath: "phones[0]", wantErr: false, }, { name: "file with path", notation: "UID123/file/folder/document.pdf", wantRef: "UID123", wantType: "file", wantPath: "folder/document.pdf", wantErr: false, }, { name: "title based", notation: "My Secret/field/password", wantRef: "My Secret", wantType: "field", wantPath: "password", wantErr: false, }, { name: "invalid - too short", notation: "UID123/field", wantRef: "", wantType: "", wantPath: "", wantErr: true, }, { name: "invalid - empty", notation: "", wantRef: "", wantType: "", wantPath: "", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotRef, gotType, gotPath, err := SplitNotationParts(tt.notation) if (err != nil) != tt.wantErr { t.Errorf("SplitNotationParts() error = %v, wantErr %v", err, tt.wantErr) return } if gotRef != tt.wantRef { t.Errorf("SplitNotationParts() ref = %v, want %v", gotRef, tt.wantRef) } if gotType != tt.wantType { t.Errorf("SplitNotationParts() type = %v, want %v", gotType, tt.wantType) } if gotPath != tt.wantPath { t.Errorf("SplitNotationParts() path = %v, want %v", gotPath, tt.wantPath) } }) } } func TestValidateNotation(t *testing.T) { tests := []struct { name string notation string wantErr bool }{ {"valid field", "UID123/field/password", false}, {"valid custom field", "UID123/custom_field/apiKey", false}, {"valid file", "UID123/file/document.pdf", false}, {"valid array", "UID123/field/url[0]", false}, {"valid property", "UID123/custom_field/name[first]", false}, {"valid nested", "UID123/custom_field/phone[0][number]", false}, {"invalid empty", "", true}, {"invalid format", "UID123", true}, {"invalid type", "UID123/invalid/password", true}, {"missing field", "UID123/field/", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateNotation(tt.notation) if (err != nil) != tt.wantErr { t.Errorf("ValidateNotation() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestRoundTripNotation(t *testing.T) { // Test that parse -> build produces the same notation notations := []string{ "NJ_xXSkk3xYI1h9ql5lAiQ/field/password", "NJ_xXSkk3xYI1h9ql5lAiQ/field/url[0]", "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/name[first]", "NJ_xXSkk3xYI1h9ql5lAiQ/custom_field/phone[0][number]", "NJ_xXSkk3xYI1h9ql5lAiQ/file/document.pdf", "My Secret/field/password", } for _, notation := range notations { t.Run(notation, func(t *testing.T) { parsed, err := ParseNotation(notation) if err != nil { t.Fatalf("Failed to parse notation: %v", err) } rebuilt := BuildNotation(parsed) if rebuilt != notation { t.Errorf("Round trip failed: got %s, want %s", rebuilt, notation) } }) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Keeper-Security/keeper-mcp-golang-docker'

If you have feedback or need assistance with the MCP directory API, please join our Discord server