feat: show Creator action history
This commit was merged in pull request #5.
This commit is contained in:
@@ -53,6 +53,43 @@ func (f fakeCreatorAuthority) LoadGrant(context.Context, authority.GrantID) (aut
|
||||
return f.grant, f.scope, nil
|
||||
}
|
||||
|
||||
func (f *fakeExecutionRepository) ListAuthorizedByOwner(context.Context, identity.ActorID, identity.PrincipalID, int) ([]commitcontract.AuthorizedAction, error) {
|
||||
if f.action.ID == "" {
|
||||
return []commitcontract.AuthorizedAction{}, nil
|
||||
}
|
||||
return []commitcontract.AuthorizedAction{f.action}, nil
|
||||
}
|
||||
|
||||
func (f *fakeExecutionRepository) FindLatestAttempt(_ context.Context, id commitcontract.AuthorizedActionID) (commitcontract.ActionAttempt, bool, error) {
|
||||
for index := len(f.attempts) - 1; index >= 0; index-- {
|
||||
if f.attempts[index].AuthorizedID == id {
|
||||
return f.attempts[index], true, nil
|
||||
}
|
||||
}
|
||||
return commitcontract.ActionAttempt{}, false, nil
|
||||
}
|
||||
|
||||
type fakeCreatorHistoryRepository struct {
|
||||
fakeExecutionRepository
|
||||
actions []commitcontract.AuthorizedAction
|
||||
latest map[commitcontract.AuthorizedActionID]commitcontract.ActionAttempt
|
||||
actorID identity.ActorID
|
||||
principalID identity.PrincipalID
|
||||
limit int
|
||||
listCalls int
|
||||
}
|
||||
|
||||
func (f *fakeCreatorHistoryRepository) ListAuthorizedByOwner(_ context.Context, actorID identity.ActorID, principalID identity.PrincipalID, limit int) ([]commitcontract.AuthorizedAction, error) {
|
||||
f.actorID, f.principalID, f.limit = actorID, principalID, limit
|
||||
f.listCalls++
|
||||
return f.actions, nil
|
||||
}
|
||||
|
||||
func (f *fakeCreatorHistoryRepository) FindLatestAttempt(_ context.Context, id commitcontract.AuthorizedActionID) (commitcontract.ActionAttempt, bool, error) {
|
||||
attempt, ok := f.latest[id]
|
||||
return attempt, ok, nil
|
||||
}
|
||||
|
||||
func TestCreatorCommandDerivesAuthorityAndActionFieldsServerSide(t *testing.T) {
|
||||
handler, repository, executor := validCreatorCommandHandler()
|
||||
mux := http.NewServeMux()
|
||||
@@ -127,6 +164,69 @@ func TestCreatorCommandRejectsSessionForeignOwnerStaleStateAndRevokedGrant(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatorActionHistoryUsesSessionOwnerAndShowsMissingAttempt(t *testing.T) {
|
||||
session := identity.CreatorSession{ActorID: "creator-actor", PrincipalID: "creator-principal", ExpiresAtUnix: 1000}
|
||||
repository := &fakeCreatorHistoryRepository{
|
||||
actions: []commitcontract.AuthorizedAction{
|
||||
{ID: "authorized-new", PrincipalID: session.PrincipalID, AuthorizedAtUnixMillis: 20, State: commitcontract.ActionAuthorized, Proposal: commitcontract.ActionProposal{ActorID: session.ActorID, IntentID: "intent-new", Operation: commitcontract.OperationSatisfyDelivered, ContinuitySubject: continuity.SatisfyDeliveredSubject{ProspectiveItemID: "prospective-new"}}},
|
||||
{ID: "authorized-pending", PrincipalID: session.PrincipalID, AuthorizedAtUnixMillis: 10, State: commitcontract.ActionAuthorized, Proposal: commitcontract.ActionProposal{ActorID: session.ActorID, IntentID: "intent-pending", Operation: commitcontract.OperationSatisfyDelivered, ContinuitySubject: continuity.SatisfyDeliveredSubject{ProspectiveItemID: "prospective-pending"}}},
|
||||
},
|
||||
latest: map[commitcontract.AuthorizedActionID]commitcontract.ActionAttempt{
|
||||
"authorized-new": {ID: "attempt-new", AuthorizedID: "authorized-new", IntentID: "intent-new", State: commitcontract.ActionSucceeded},
|
||||
},
|
||||
}
|
||||
handler := creatorCommandHandler{core: fakeCreatorCore{session: session}, repository: repository}
|
||||
mux := http.NewServeMux()
|
||||
registerCreatorCommandRoutes(mux, handler)
|
||||
request := httptest.NewRequest(http.MethodGet, "/v1/creator/authorized-actions?limit=2", nil)
|
||||
request.Header.Set("Authorization", "Bearer creator-session")
|
||||
response := httptest.NewRecorder()
|
||||
mux.ServeHTTP(response, request)
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
var result creatorActionHistoryResponse
|
||||
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if repository.actorID != session.ActorID || repository.principalID != session.PrincipalID || repository.limit != 2 || repository.listCalls != 1 {
|
||||
t.Fatalf("owner query actor=%q principal=%q limit=%d calls=%d", repository.actorID, repository.principalID, repository.limit, repository.listCalls)
|
||||
}
|
||||
if len(result.Items) != 2 || result.Items[0].State != commitcontract.ActionSucceeded || result.Items[0].ActionAttemptID != "attempt-new" ||
|
||||
result.Items[1].State != commitcontract.ActionAuthorized || result.Items[1].ActionAttemptID != "" {
|
||||
t.Fatalf("history=%#v", result.Items)
|
||||
}
|
||||
if strings.Contains(response.Body.String(), "EvidenceRef") || strings.Contains(response.Body.String(), "GrantID") {
|
||||
t.Fatalf("history leaked restricted envelope fields: %s", response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatorActionHistoryRejectsSessionAndInvalidLimitBeforeQuery(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
sessionErr error
|
||||
path string
|
||||
status int
|
||||
}{
|
||||
{name: "session", sessionErr: errCreatorSessionDenied, path: "/v1/creator/authorized-actions", status: http.StatusUnauthorized},
|
||||
{name: "zero", path: "/v1/creator/authorized-actions?limit=0", status: http.StatusBadRequest},
|
||||
{name: "too large", path: "/v1/creator/authorized-actions?limit=51", status: http.StatusBadRequest},
|
||||
{name: "invalid", path: "/v1/creator/authorized-actions?limit=many", status: http.StatusBadRequest},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repository := &fakeCreatorHistoryRepository{}
|
||||
handler := creatorCommandHandler{core: fakeCreatorCore{session: identity.CreatorSession{ActorID: "actor", PrincipalID: "principal"}, sessionErr: test.sessionErr}, repository: repository}
|
||||
mux := http.NewServeMux()
|
||||
registerCreatorCommandRoutes(mux, handler)
|
||||
response := httptest.NewRecorder()
|
||||
mux.ServeHTTP(response, httptest.NewRequest(http.MethodGet, test.path, nil))
|
||||
if response.Code != test.status || repository.listCalls != 0 {
|
||||
t.Fatalf("status=%d calls=%d", response.Code, repository.listCalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func validCreatorCommandHandler() (creatorCommandHandler, *fakeExecutionRepository, *knownExecutor) {
|
||||
session := identity.CreatorSession{ActorID: "creator-actor", PrincipalID: "creator-principal", ExpiresAtUnix: 1000}
|
||||
target := work.TaskTarget{TaskID: "task-1", GoalID: "goal-1", ScopeID: "scope-1", Revision: 3}
|
||||
|
||||
Reference in New Issue
Block a user