package extsvc import ( "context" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" ) // The verb, the key and the record must arrive in the shape HAMLOG's own agent // sends — this is read from their client, not from documentation, so the test // pins it rather than trusting a memory of it. func TestUploadHamlogRequestShape(t *testing.T) { var got map[string]map[string]any srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("method %s, want POST", r.Method) } if ct := r.Header.Get("Content-Type"); !strings.Contains(ct, "application/json") { t.Errorf("Content-Type %q", ct) } b, _ := io.ReadAll(r.Body) _ = json.Unmarshal(b, &got) _, _ = w.Write([]byte(`{"STATUS":"OK"}`)) })) defer srv.Close() res, err := uploadHamlogTo(context.Background(), nil, srv.URL, ServiceConfig{APIKey: "KEY123"}, "F4BPO ") if err != nil { t.Fatal(err) } if !res.OK { t.Fatalf("upload not OK: %+v", res) } add, ok := got["ADIFADD"] if !ok { t.Fatalf("no ADIFADD verb in %v", got) } if add["APIKEY"] != "KEY123" { t.Errorf("APIKEY = %v", add["APIKEY"]) } if add["ADIFDATA"] != "F4BPO " { t.Errorf("ADIFDATA = %v", add["ADIFDATA"]) } } // A refusal must be reported as a refusal. Their failure shape carries ERROR // and no STATUS, so "no error field" would have read an unknown reply as an // accepted QSO — which is how a contact goes missing without anyone noticing. func TestHamlogFailureIsNotSuccess(t *testing.T) { for _, tc := range []struct { body string wantOK bool wantSaid string }{ {`{"STATUS":"OK"}`, true, ""}, {`{"ERROR":"Invalid API key"}`, false, "Invalid API key"}, {`{"STATUS":"FAILED"}`, false, "rejected"}, {`{}`, false, "rejected"}, // an empty object is not an acceptance } { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(tc.body)) })) res, err := uploadHamlogTo(context.Background(), nil, srv.URL, ServiceConfig{APIKey: "K"}, "") srv.Close() if err != nil { t.Fatalf("%s: %v", tc.body, err) } if res.OK != tc.wantOK { t.Errorf("%s → OK=%v, want %v", tc.body, res.OK, tc.wantOK) } if !tc.wantOK && res.Message != tc.wantSaid { t.Errorf("%s → message %q, want %q", tc.body, res.Message, tc.wantSaid) } } } // Nothing leaves without a key, and the message says where to get one. func TestUploadHamlogNeedsAKey(t *testing.T) { _, err := UploadHamlog(context.Background(), nil, ServiceConfig{}, "") if err == nil || !strings.Contains(err.Error(), hamlogKeyPage) { t.Fatalf("err = %v, want it to point at %s", err, hamlogKeyPage) } }