98 lines
4.0 KiB
Go
98 lines
4.0 KiB
Go
package controlplane
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCancellationLeaseRenewalAndExpiredGeneration(t *testing.T) {
|
|
server, err := NewServer(ServerConfig{
|
|
DataFile: filepath.Join(t.TempDir(), "state.json"),
|
|
NodeTokens: map[string]string{"node-1": "node-secret"},
|
|
WebUsers: map[string]string{"admin": "web-secret"},
|
|
LeaseTTL: time.Minute,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
httpServer := httptest.NewServer(server.Handler())
|
|
defer httpServer.Close()
|
|
client := httpServer.Client()
|
|
login := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/auth/login", "", map[string]string{"username": "admin", "password": "web-secret"})
|
|
var session struct {
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
decodeBody(t, login, &session)
|
|
webAuth := "Bearer " + session.AccessToken
|
|
|
|
registration := NodeRegistration{NodeID: "node-1", AgentVersion: "test", ProtocolVersion: ProtocolVersion,
|
|
Accounts: []AccountSummary{{AccountID: "account-a", Active: true, Verified: true}}}
|
|
if response := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/nodes/register", "Bearer node-secret", registration); response.Code != http.StatusOK {
|
|
t.Fatal(response.Code)
|
|
}
|
|
create := func(key string) TaskSubmissionResponse {
|
|
response := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/tasks", webAuth, TaskSubmission{
|
|
NodeID: "node-1", AccountID: "account-a", Kind: "send-text", IdempotencyKey: key,
|
|
Payload: json.RawMessage(`{"target_id":"target","text":"text","confirmed":true}`),
|
|
})
|
|
var result TaskSubmissionResponse
|
|
decodeBody(t, response, &result)
|
|
return result
|
|
}
|
|
|
|
cancelled := create("cancel-before-ack")
|
|
poll := doJSON(t, client, http.MethodGet, httpServer.URL+"/v1/nodes/node-1/tasks?account_id=account-a", "Bearer node-secret", nil)
|
|
var batch TaskBatch
|
|
decodeBody(t, poll, &batch)
|
|
if len(batch.Tasks) != 1 {
|
|
t.Fatalf("expected one task, got %d", len(batch.Tasks))
|
|
}
|
|
lease := batch.Tasks[0]
|
|
cancel := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/tasks/"+cancelled.TaskID+"/cancel", webAuth, nil)
|
|
var cancelledTask Task
|
|
decodeBody(t, cancel, &cancelledTask)
|
|
if cancelledTask.Status != TaskPending || cancelledTask.CancelRequestedAt == nil {
|
|
t.Fatalf("cancel mutated primary state: %+v", cancelledTask)
|
|
}
|
|
ack := TaskAck{TaskID: lease.TaskID, AccountID: lease.AccountID, LeaseGeneration: lease.LeaseGeneration}
|
|
ackResponse := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/nodes/node-1/tasks/"+lease.TaskID+"/ack", "Bearer node-secret", ack)
|
|
var acked Task
|
|
decodeBody(t, ackResponse, &acked)
|
|
if acked.Status != TaskCancelled {
|
|
t.Fatalf("cancel was not confirmed at safe ack point: %+v", acked)
|
|
}
|
|
|
|
running := create("renew-and-stale")
|
|
poll = doJSON(t, client, http.MethodGet, httpServer.URL+"/v1/nodes/node-1/tasks?account_id=account-a", "Bearer node-secret", nil)
|
|
batch = TaskBatch{}
|
|
decodeBody(t, poll, &batch)
|
|
var runningLease Task
|
|
for _, task := range batch.Tasks {
|
|
if task.TaskID == running.TaskID {
|
|
runningLease = task
|
|
}
|
|
}
|
|
if runningLease.TaskID == "" {
|
|
t.Fatal("running task was not offered")
|
|
}
|
|
ack = TaskAck{TaskID: runningLease.TaskID, AccountID: runningLease.AccountID, LeaseGeneration: runningLease.LeaseGeneration}
|
|
for _, phase := range []string{"ack", "start", "renew"} {
|
|
response := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/nodes/node-1/tasks/"+runningLease.TaskID+"/"+phase, "Bearer node-secret", ack)
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("%s status = %d: %s", phase, response.Code, response.Body.String())
|
|
}
|
|
}
|
|
stale := ack
|
|
stale.LeaseGeneration++
|
|
staleResponse := doJSON(t, client, http.MethodPost, httpServer.URL+"/v1/nodes/node-1/tasks/"+runningLease.TaskID+"/result", "Bearer node-secret", TaskResult{
|
|
TaskID: runningLease.TaskID, AccountID: runningLease.AccountID, LeaseGeneration: stale.LeaseGeneration, Status: TaskSucceeded, CorrelationID: "stale",
|
|
})
|
|
if staleResponse.Code != http.StatusConflict {
|
|
t.Fatalf("stale result status = %d", staleResponse.Code)
|
|
}
|
|
}
|