package service import ( "bytes" "context" "encoding/json" "io" "net/http" "testing" "time" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) } func TestAWXClientLaunchAndGetJob(t *testing.T) { client := NewAWXClient("https://awx.example", "token") client.client.Transport = roundTripFunc(func(r *http.Request) (*http.Response, error) { if r.Header.Get("Authorization") != "Bearer token" { t.Fatalf("missing auth header") } job := AWXJob{ID: 42, Status: "pending"} if r.URL.Path == "/api/v2/jobs/42/" { job.Status = "successful" } else if r.URL.Path != "/api/v2/job_templates/7/launch/" { t.Fatalf("unexpected path %s", r.URL.Path) } raw, _ := json.Marshal(job) return &http.Response{StatusCode: http.StatusOK, Status: "200 OK", Body: io.NopCloser(bytes.NewReader(raw)), Header: make(http.Header)}, nil }) job, err := client.Launch(context.Background(), 7, AWXLaunchRequest{InventoryID: 3, ExtraVars: map[string]any{"task_id": "t"}}) if err != nil || job.ID != 42 { t.Fatalf("launch = %#v, err=%v", job, err) } job, err = client.GetJob(context.Background(), "42") if err != nil || job.Status != "successful" { t.Fatalf("get job = %#v, err=%v", job, err) } } func TestAWXClientCreatesTokenFromCredentials(t *testing.T) { client := NewAWXClient("https://awx.example", "", "admin", "password") tokenRequests := 0 client.client.Transport = roundTripFunc(func(r *http.Request) (*http.Response, error) { switch r.URL.Path { case "/api/v2/tokens/": tokenRequests++ username, password, ok := r.BasicAuth() if !ok || username != "admin" || password != "password" { t.Fatalf("missing basic auth for token request") } raw, _ := json.Marshal(awxTokenResponse{Token: "generated-token", Expires: time.Now().Add(time.Hour).Format(time.RFC3339)}) return &http.Response{StatusCode: http.StatusCreated, Status: "201 Created", Body: io.NopCloser(bytes.NewReader(raw)), Header: make(http.Header)}, nil case "/api/v2/jobs/42/": if r.Header.Get("Authorization") != "Bearer generated-token" { t.Fatalf("missing generated bearer token") } raw, _ := json.Marshal(AWXJob{ID: 42, Status: "successful"}) return &http.Response{StatusCode: http.StatusOK, Status: "200 OK", Body: io.NopCloser(bytes.NewReader(raw)), Header: make(http.Header)}, nil default: t.Fatalf("unexpected path %s", r.URL.Path) return nil, nil } }) if _, err := client.GetJob(context.Background(), "42"); err != nil { t.Fatalf("get job: %v", err) } if _, err := client.GetJob(context.Background(), "42"); err != nil { t.Fatalf("get job with cached token: %v", err) } if tokenRequests != 1 { t.Fatalf("token requests = %d, want 1", tokenRequests) } } func TestAWXHostIPPrefersXInfraPublicAddress(t *testing.T) { host := AWXInventoryHost{ Name: "postgresql-218-11-5-224", Variables: `{"ansible_host":"127.0.0.1","xinfra_public_address":"218.11.5.224"}`, } if got := AWXHostIP(host); got != "218.11.5.224" { t.Fatalf("AWXHostIP = %q, want public address", got) } }