HAMLOG.online no longer issues API keys, and its upload API takes nothing else. An operator without a key cannot obtain one, so the auto-upload switch, the on-close sweep and the 'Send to' entry were all arming something that could only fail — silently, once per QSO. Closed at the source rather than hidden in the UI: the upload returns a sentinel that says why, the manager stops routing to it and says so once a session, and the manual path refuses with the same words. The settings page states it plainly instead of showing a switch that does nothing. Nothing else goes. Their confirmations arrive as an ADIF FILE and never needed a key, so that import stays; the sent/received state already in operators' logs stays readable, filterable and bulk-editable; and the upload itself is kept whole as uploadHamlogLive, still covered by its request-shape tests, against the day keys come back.
102 lines
3.3 KiB
Go
102 lines
3.3 KiB
Go
package extsvc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"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 := uploadHamlogLive(context.Background(), nil, srv.URL, ServiceConfig{APIKey: "KEY123"}, "<call:5>F4BPO <eor>")
|
|
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"] != "<call:5>F4BPO <eor>" {
|
|
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 := uploadHamlogLive(context.Background(), nil, srv.URL, ServiceConfig{APIKey: "K"}, "<eor>")
|
|
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 := uploadHamlogLive(context.Background(), nil, "http://example.invalid", ServiceConfig{}, "<eor>")
|
|
if err == nil || !strings.Contains(err.Error(), hamlogKeyPage) {
|
|
t.Fatalf("err = %v, want it to point at %s", err, hamlogKeyPage)
|
|
}
|
|
}
|
|
|
|
// The door is shut: HAMLOG.online stopped issuing API keys, so an upload that
|
|
// could only fail is refused before it is attempted. The request-shape tests
|
|
// above still cover the code kept against the day keys come back.
|
|
func TestUploadHamlogIsClosed(t *testing.T) {
|
|
res, err := UploadHamlog(context.Background(), nil, ServiceConfig{APIKey: "KEY123"}, "<eor>")
|
|
if !errors.Is(err, ErrHamlogClosed) {
|
|
t.Fatalf("err = %v, want ErrHamlogClosed", err)
|
|
}
|
|
if res.OK {
|
|
t.Error("a refused upload reported success")
|
|
}
|
|
}
|