Prove A can run the full action chain and outsiders stay denied; assert audit Details carry duty_policy on all five transitions. Co-authored-by: Cursor <cursoragent@cursor.com>
124 lines
5.1 KiB
Go
124 lines
5.1 KiB
Go
package projectinstance
|
|
|
|
import "testing"
|
|
|
|
func TestRequiredDutyRole(t *testing.T) {
|
|
if requiredDutyRole(DocAccessActionRun) != DutyRoleAuthor {
|
|
t.Fatal("run -> author")
|
|
}
|
|
if requiredDutyRole(DocAccessActionEdit) != DutyRoleAuthor {
|
|
t.Fatal("edit -> author")
|
|
}
|
|
if requiredDutyRole(DocAccessActionSubmit) != DutyRoleAuthor {
|
|
t.Fatal("submit -> author")
|
|
}
|
|
if requiredDutyRole(DocAccessActionReview) != DutyRoleReviewer {
|
|
t.Fatal("review -> reviewer")
|
|
}
|
|
if requiredDutyRole(DocAccessActionApprove) != DutyRoleApprover {
|
|
t.Fatal("approve -> approver")
|
|
}
|
|
if requiredDutyRole(DocAccessActionView) != "" {
|
|
t.Fatal("view has no duty role")
|
|
}
|
|
}
|
|
|
|
func TestClassifyAccess_FullDutyMatrix(t *testing.T) {
|
|
dt := DocTaskPlan{Enabled: true, Duties: DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3}}
|
|
creatorID := int64(9)
|
|
|
|
cases := []struct {
|
|
name string
|
|
userID int64
|
|
isCreator bool
|
|
action string
|
|
wantAllow bool
|
|
wantCode string
|
|
}{
|
|
{"author run", 1, false, DocAccessActionRun, true, ""},
|
|
{"author edit", 1, false, DocAccessActionEdit, true, ""},
|
|
{"author submit", 1, false, DocAccessActionSubmit, true, ""},
|
|
{"author review denied", 1, false, DocAccessActionReview, false, AccessCodeForbiddenDuty},
|
|
{"author approve denied", 1, false, DocAccessActionApprove, false, AccessCodeForbiddenDuty},
|
|
{"reviewer run denied", 2, false, DocAccessActionRun, false, AccessCodeForbiddenDuty},
|
|
{"reviewer edit denied", 2, false, DocAccessActionEdit, false, AccessCodeForbiddenDuty},
|
|
{"reviewer submit denied", 2, false, DocAccessActionSubmit, false, AccessCodeForbiddenDuty},
|
|
{"reviewer review", 2, false, DocAccessActionReview, true, ""},
|
|
{"approver run denied", 3, false, DocAccessActionRun, false, AccessCodeForbiddenDuty},
|
|
{"approver edit denied", 3, false, DocAccessActionEdit, false, AccessCodeForbiddenDuty},
|
|
{"approver review denied", 3, false, DocAccessActionReview, false, AccessCodeForbiddenDuty},
|
|
{"approver approve", 3, false, DocAccessActionApprove, true, ""},
|
|
{"creator view", creatorID, true, DocAccessActionView, true, ""},
|
|
{"creator run denied", creatorID, true, DocAccessActionRun, false, AccessCodeForbiddenDuty},
|
|
{"creator submit denied", creatorID, true, DocAccessActionSubmit, false, AccessCodeForbiddenDuty},
|
|
{"creator review denied", creatorID, true, DocAccessActionReview, false, AccessCodeForbiddenDuty},
|
|
{"creator approve denied", creatorID, true, DocAccessActionApprove, false, AccessCodeForbiddenDuty},
|
|
{"reviewer cannot re-run after reject", 2, false, DocAccessActionRun, false, AccessCodeForbiddenDuty},
|
|
}
|
|
for _, tc := range cases {
|
|
got := classifyAccess(dt, tc.action, tc.userID, tc.isCreator, true)
|
|
if got.Allowed != tc.wantAllow || (tc.wantCode != "" && got.Code != tc.wantCode) {
|
|
t.Fatalf("%s: got %+v want allow=%v code=%s", tc.name, got, tc.wantAllow, tc.wantCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestClassifyAccess_NoneAndPreActivation(t *testing.T) {
|
|
none := DocTaskPlan{Enabled: true, Duties: DocDuty{}}
|
|
creatorRun := classifyAccess(none, DocAccessActionRun, 1, true, true)
|
|
if !creatorRun.Allowed {
|
|
t.Fatalf("creator run on none: %+v", creatorRun)
|
|
}
|
|
creatorEdit := classifyAccess(none, DocAccessActionEdit, 1, true, true)
|
|
if !creatorEdit.Allowed {
|
|
t.Fatalf("creator edit on none: %+v", creatorEdit)
|
|
}
|
|
memberRun := classifyAccess(none, DocAccessActionRun, 2, false, true)
|
|
if memberRun.Allowed || memberRun.Code != AccessCodeForbiddenDuty {
|
|
t.Fatalf("member run on none: %+v", memberRun)
|
|
}
|
|
submit := classifyAccess(none, DocAccessActionSubmit, 1, true, true)
|
|
if submit.Allowed || submit.Code != AccessCodeDutyNotManaged {
|
|
t.Fatalf("submit on none: %+v", submit)
|
|
}
|
|
preRun := classifyAccess(none, DocAccessActionRun, 1, true, false)
|
|
if !preRun.Allowed || preRun.Code != AccessCodeNotActivated {
|
|
t.Fatalf("pre-activation run: %+v", preRun)
|
|
}
|
|
preSubmit := classifyAccess(none, DocAccessActionSubmit, 1, true, false)
|
|
if preSubmit.Allowed || preSubmit.Code != AccessCodeNotActivated {
|
|
t.Fatalf("pre-activation submit: %+v", preSubmit)
|
|
}
|
|
}
|
|
|
|
func TestClassifyAccess_AuthorOnlyMatrix(t *testing.T) {
|
|
dt := DocTaskPlan{
|
|
Enabled: true,
|
|
DutyPolicy: DutyPolicyAuthorOnly,
|
|
Duties: DocDuty{AuthorUserID: 7},
|
|
}
|
|
cases := []struct {
|
|
name string
|
|
userID int64
|
|
action string
|
|
wantAllow bool
|
|
wantCode string
|
|
}{
|
|
{"author run", 7, DocAccessActionRun, true, ""},
|
|
{"author edit", 7, DocAccessActionEdit, true, ""},
|
|
{"author submit", 7, DocAccessActionSubmit, true, ""},
|
|
{"author review", 7, DocAccessActionReview, true, ""},
|
|
{"author approve", 7, DocAccessActionApprove, true, ""},
|
|
{"other run denied", 2, DocAccessActionRun, false, AccessCodeForbiddenDuty},
|
|
{"other review denied", 2, DocAccessActionReview, false, AccessCodeForbiddenDuty},
|
|
{"other approve denied", 3, DocAccessActionApprove, false, AccessCodeForbiddenDuty},
|
|
{"creator cannot elevate", 9, DocAccessActionApprove, false, AccessCodeForbiddenDuty},
|
|
}
|
|
for _, tc := range cases {
|
|
got := classifyAccess(dt, tc.action, tc.userID, tc.userID == 9, true)
|
|
if got.Allowed != tc.wantAllow || (tc.wantCode != "" && got.Code != tc.wantCode) {
|
|
t.Fatalf("%s: got %+v want allow=%v code=%s", tc.name, got, tc.wantAllow, tc.wantCode)
|
|
}
|
|
}
|
|
}
|