test(linear): add comprehensive tests for mapping and client functions

Added tests for:
- DefaultMappingConfig
- PriorityToBeads/PriorityToLinear
- StateToBeadsStatus/ParseBeadsStatus
- StatusToLinearStateType
- LabelToIssueType/ParseIssueType
- RelationToBeadsDep
- IssueToBeads (with parent/child handling)
- BuildLinearToLocalUpdates
- LoadMappingConfig
- BuildLinearDescription
- NewClient/WithEndpoint/WithHTTPClient
- ExtractLinearIdentifier
- IsLinearExternalRef

Linear package coverage improved from 14.3% to ~50%.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-19 18:30:03 -08:00
parent cc5893656b
commit af432bee4e
2 changed files with 589 additions and 1 deletions

View File

@@ -1,6 +1,10 @@
package linear
import "testing"
import (
"net/http"
"testing"
"time"
)
func TestCanonicalizeLinearExternalRef(t *testing.T) {
tests := []struct {
@@ -39,3 +43,130 @@ func TestCanonicalizeLinearExternalRef(t *testing.T) {
}
}
}
func TestNewClient(t *testing.T) {
client := NewClient("test-api-key", "test-team-id")
if client.APIKey != "test-api-key" {
t.Errorf("APIKey = %q, want %q", client.APIKey, "test-api-key")
}
if client.TeamID != "test-team-id" {
t.Errorf("TeamID = %q, want %q", client.TeamID, "test-team-id")
}
if client.Endpoint != DefaultAPIEndpoint {
t.Errorf("Endpoint = %q, want %q", client.Endpoint, DefaultAPIEndpoint)
}
if client.HTTPClient == nil {
t.Error("HTTPClient should not be nil")
}
}
func TestWithEndpoint(t *testing.T) {
client := NewClient("key", "team")
customEndpoint := "https://custom.linear.app/graphql"
newClient := client.WithEndpoint(customEndpoint)
if newClient.Endpoint != customEndpoint {
t.Errorf("Endpoint = %q, want %q", newClient.Endpoint, customEndpoint)
}
// Original should be unchanged
if client.Endpoint != DefaultAPIEndpoint {
t.Errorf("Original endpoint changed: %q", client.Endpoint)
}
// Other fields preserved
if newClient.APIKey != "key" {
t.Errorf("APIKey not preserved: %q", newClient.APIKey)
}
}
func TestWithHTTPClient(t *testing.T) {
client := NewClient("key", "team")
customHTTPClient := &http.Client{Timeout: 60 * time.Second}
newClient := client.WithHTTPClient(customHTTPClient)
if newClient.HTTPClient != customHTTPClient {
t.Error("HTTPClient not set correctly")
}
// Other fields preserved
if newClient.APIKey != "key" {
t.Errorf("APIKey not preserved: %q", newClient.APIKey)
}
if newClient.Endpoint != DefaultAPIEndpoint {
t.Errorf("Endpoint not preserved: %q", newClient.Endpoint)
}
}
func TestExtractLinearIdentifier(t *testing.T) {
tests := []struct {
name string
externalRef string
want string
}{
{
name: "standard URL",
externalRef: "https://linear.app/team/issue/PROJ-123",
want: "PROJ-123",
},
{
name: "URL with slug",
externalRef: "https://linear.app/team/issue/PROJ-456/some-title-here",
want: "PROJ-456",
},
{
name: "URL with trailing slash",
externalRef: "https://linear.app/team/issue/ABC-789/",
want: "ABC-789",
},
{
name: "non-linear URL",
externalRef: "https://jira.example.com/browse/PROJ-123",
want: "",
},
{
name: "empty string",
externalRef: "",
want: "",
},
{
name: "malformed URL",
externalRef: "not-a-url",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ExtractLinearIdentifier(tt.externalRef)
if got != tt.want {
t.Errorf("ExtractLinearIdentifier(%q) = %q, want %q", tt.externalRef, got, tt.want)
}
})
}
}
func TestIsLinearExternalRef(t *testing.T) {
tests := []struct {
ref string
want bool
}{
{"https://linear.app/team/issue/PROJ-123", true},
{"https://linear.app/team/issue/PROJ-123/slug", true},
{"https://jira.example.com/browse/PROJ-123", false},
{"https://github.com/org/repo/issues/123", false},
{"", false},
}
for _, tt := range tests {
t.Run(tt.ref, func(t *testing.T) {
got := IsLinearExternalRef(tt.ref)
if got != tt.want {
t.Errorf("IsLinearExternalRef(%q) = %v, want %v", tt.ref, got, tt.want)
}
})
}
}
// Note: BuildStateCache and FindStateForBeadsStatus require API calls
// and would need mocking to test. Skipping unit tests for those.