Files
2026-08-16 16:24:02 +03:00

78 lines
3.1 KiB
Go

package main
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.cin.su/m/Assistant/adapters/postgres"
commitcontract "git.cin.su/m/Assistant/contracts/commit"
"git.cin.su/m/Assistant/modules/commit/ports"
)
type fakeExecutionRepository struct {
action commitcontract.AuthorizedAction
attempts []commitcontract.ActionAttempt
}
func (f *fakeExecutionRepository) LoadAuthorized(context.Context, commitcontract.AuthorizedActionID) (commitcontract.AuthorizedAction, error) {
return f.action, nil
}
func (f *fakeExecutionRepository) LoadLatestAttempt(context.Context, commitcontract.AuthorizedActionID) (commitcontract.ActionAttempt, error) {
if len(f.attempts) == 0 {
return commitcontract.ActionAttempt{}, postgres.ErrActionAttemptNotFound
}
return f.attempts[len(f.attempts)-1], nil
}
func (f *fakeExecutionRepository) BeginAttempt(_ context.Context, attempt commitcontract.ActionAttempt) error {
f.attempts = append(f.attempts, attempt)
return nil
}
func (f *fakeExecutionRepository) FinishAttempt(_ context.Context, attempt commitcontract.ActionAttempt) error {
if len(f.attempts) == 0 || f.attempts[len(f.attempts)-1].ID != attempt.ID {
return errors.New("attempt not begun")
}
f.attempts[len(f.attempts)-1] = attempt
return nil
}
func (f *fakeExecutionRepository) SaveAuthorized(_ context.Context, action commitcontract.AuthorizedAction) error {
f.action = action
return nil
}
type knownExecutor struct{ calls int }
func (e *knownExecutor) Execute(commitcontract.AuthorizedAction) ports.ExecutionResult {
e.calls++
return ports.ExecutionResult{Known: true, Succeeded: true}
}
func (e *knownExecutor) Reconcile(commitcontract.AuthorizedAction) ports.ExecutionResult {
return ports.ExecutionResult{Known: true, Succeeded: true}
}
func TestExecutionIsDurablyBegunAndCannotBeBlindlyRepeated(t *testing.T) {
repository := &fakeExecutionRepository{action: commitcontract.AuthorizedAction{ID: "authorized-1", State: commitcontract.ActionAuthorized, Proposal: commitcontract.ActionProposal{IntentID: "intent-1"}}}
executor := &knownExecutor{}
mux := http.NewServeMux()
registerExecutionRoutes(mux, repository, executor, executor, "secret")
request := httptest.NewRequest(http.MethodPost, "/v1/internal/authorized-actions/authorized-1/execute", strings.NewReader(`{"AttemptID":"attempt-1"}`))
request.Header.Set("Authorization", "Bearer secret")
result := httptest.NewRecorder()
mux.ServeHTTP(result, request)
if result.Code != http.StatusOK || executor.calls != 1 || repository.attempts[0].State != commitcontract.ActionSucceeded {
t.Fatalf("status=%d calls=%d attempts=%#v", result.Code, executor.calls, repository.attempts)
}
repeated := httptest.NewRequest(http.MethodPost, "/v1/internal/authorized-actions/authorized-1/execute", strings.NewReader(`{"AttemptID":"attempt-2"}`))
repeated.Header.Set("Authorization", "Bearer secret")
repeatedResult := httptest.NewRecorder()
mux.ServeHTTP(repeatedResult, repeated)
if repeatedResult.Code != http.StatusConflict || executor.calls != 1 {
t.Fatalf("repeat status=%d calls=%d", repeatedResult.Code, executor.calls)
}
}