feat: initial Go/JS/Rust Origin check-in client SDKs
CI / rust (push) Successful in 3m6s
CI / go (push) Successful in 33s
CI / js (push) Successful in 34s

Standalone clients that resolve a machine unique ID (OS machine ID first,
falling back to a generated UUID persisted to disk), detect hostname/IP/
container/orchestrator, and register+re-ping origin.warky.dev's public
service-checkin endpoint on a daily interval. Ported from icy2's internal
origincheckin package but extended to the full check-in contract and made
dependency-light for external reuse.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 15:25:30 +02:00
co-authored by Claude Sonnet 5
parent 282f76a021
commit 2590e404f5
40 changed files with 4105 additions and 181 deletions
+148
View File
@@ -0,0 +1,148 @@
package originclient
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func testConfig(url string) Config {
return Config{
URL: url,
ServiceKey: "test-key",
Type: "icy2",
Version: "v1.0.0",
Name: "icy2-test",
Site: "Test Site",
Environment: "test",
IntervalHours: 24,
InstallID: "test-install-id",
}
}
func TestNew_RequiresFields(t *testing.T) {
_, err := New(Config{})
assert.Error(t, err)
}
func TestNew_AppliesDefaults(t *testing.T) {
c, err := New(Config{ServiceKey: "k", Type: "t", Version: "v", Name: "n", InstallID: "id"})
require.NoError(t, err)
assert.Equal(t, DefaultURL, c.cfg.URL)
assert.Equal(t, "go", c.cfg.AppType)
assert.Equal(t, DefaultIntervalHours, c.cfg.IntervalHours)
assert.Equal(t, "id", c.installID)
}
func TestBuildPayload(t *testing.T) {
c, err := New(testConfig("https://example.invalid/checkin"))
require.NoError(t, err)
payload := c.buildPayload()
assert.Equal(t, "icy2", payload.Type)
assert.Equal(t, "go", payload.AppType)
assert.Equal(t, "v1.0.0", payload.Version)
assert.Equal(t, "icy2-test", payload.Name)
assert.Equal(t, "Test Site", payload.Site)
assert.Equal(t, "test", payload.Environment)
assert.Equal(t, "test-install-id", payload.UniqueInstallID)
}
func TestCheckinOnce_Success(t *testing.T) {
var gotKey string
var gotBody Payload
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotKey = r.Header.Get("X-Origin-Service-Key")
require.NoError(t, json.NewDecoder(r.Body).Decode(&gotBody))
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(Response{
Success: true,
ServiceInstanceID: 42,
UniqueInstallID: gotBody.UniqueInstallID,
Status: "provisioning",
PingedAt: "2026-08-01T00:00:00Z",
})
}))
defer server.Close()
c, err := New(testConfig(server.URL))
require.NoError(t, err)
resp, err := c.CheckinOnce(t.Context())
require.NoError(t, err)
assert.Equal(t, "test-key", gotKey)
assert.Equal(t, "icy2", gotBody.Type)
assert.Equal(t, "icy2-test", gotBody.Name)
assert.Equal(t, "test-install-id", gotBody.UniqueInstallID)
assert.True(t, resp.Success)
assert.Equal(t, int64(42), resp.ServiceInstanceID)
}
func TestCheckinOnce_ServerErrorReturnsError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer server.Close()
c, err := New(testConfig(server.URL))
require.NoError(t, err)
resp, err := c.CheckinOnce(t.Context())
assert.Error(t, err)
assert.Nil(t, resp)
}
func TestCheckinOnce_UnreachableDoesNotPanic(t *testing.T) {
c, err := New(testConfig("http://127.0.0.1:1"))
require.NoError(t, err)
assert.NotPanics(t, func() {
_, _ = c.CheckinOnce(t.Context())
})
}
func TestCheckinOnce_LogsOnFailure(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer server.Close()
cfg := testConfig(server.URL)
var warned bool
cfg.Logger = LoggerFunc(func(msg string, err error) { warned = true })
c, err := New(cfg)
require.NoError(t, err)
_, _ = c.CheckinOnce(t.Context())
assert.True(t, warned)
}
func TestRun_ChecksInImmediatelyThenStopsOnContextDone(t *testing.T) {
var calls atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls.Add(1)
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(Response{Success: true})
}))
defer server.Close()
c, err := New(testConfig(server.URL))
require.NoError(t, err)
ctx, cancel := context.WithTimeout(t.Context(), 200*time.Millisecond)
defer cancel()
c.Run(ctx)
assert.Equal(t, int32(1), calls.Load())
}