verify / verify (push) Successful in 3m2s
Sanitized allowlisted snapshot of private source 0acd276fb3423405daf7fff26dedc92b8281e2bd. Adds organization, team, invitation, resource hierarchy, scoped access, and audited break-glass foundations while preserving Preview 1. AI-Assistance: OpenAI Codex assisted implementation, testing, security review, and release preparation. Signed-off-by: Cole Speelman <crspeelman@gmail.com>
86 lines
3.2 KiB
Go
86 lines
3.2 KiB
Go
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package organizations
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCreateInviteAndAccept(t *testing.T) {
|
|
now := time.Unix(1000, 0).UTC()
|
|
repository := &repositoryStub{}
|
|
service, err := New(repository, Options{Random: strings.NewReader(strings.Repeat("r", 512)), Now: func() time.Time { return now }})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
organization, err := service.CreateOrganization(t.Context(), CreateOrganization{Slug: "quiet-systems", Name: "Quiet Systems", OwnerUserID: "user-12345678"})
|
|
if err != nil || organization.ID == "" || repository.organization.ID != organization.ID {
|
|
t.Fatalf("organization=%+v err=%v", organization, err)
|
|
}
|
|
raw, invitation, err := service.Invite(t.Context(), organization.ID, "MEMBER@example.test", "user-12345678", time.Hour)
|
|
if err != nil || raw == "" || invitation.Email != "member@example.test" {
|
|
t.Fatalf("invitation=%+v err=%v", invitation, err)
|
|
}
|
|
repository.invitation = invitation
|
|
if err = service.AcceptInvitation(t.Context(), raw, "user-87654321"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if repository.acceptedUser != "user-87654321" {
|
|
t.Fatalf("accepted=%q", repository.acceptedUser)
|
|
}
|
|
}
|
|
|
|
func TestInvitationFailsClosed(t *testing.T) {
|
|
service, err := New(&repositoryStub{invitationErr: ErrInvitationNotFound}, Options{Random: strings.NewReader(strings.Repeat("x", 256))})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = service.AcceptInvitation(t.Context(), strings.Repeat("x", 43), "user-87654321"); !errors.Is(err, ErrInvitationNotFound) {
|
|
t.Fatalf("err=%v", err)
|
|
}
|
|
if _, _, err = service.Invite(t.Context(), "bad", "person@example.test", "user-12345678", time.Hour); err == nil {
|
|
t.Fatal("invalid organization accepted")
|
|
}
|
|
}
|
|
|
|
type repositoryStub struct {
|
|
organization Organization
|
|
invitation Invitation
|
|
invitationErr error
|
|
acceptedUser string
|
|
}
|
|
|
|
func (repository *repositoryStub) CreateOrganization(_ context.Context, organization Organization, _ Membership) error {
|
|
repository.organization = organization
|
|
return nil
|
|
}
|
|
func (*repositoryStub) CreateTeam(context.Context, Team) error { return nil }
|
|
func (*repositoryStub) AddTeamMember(context.Context, TeamMembership) error { return nil }
|
|
func (*repositoryStub) CreateProject(context.Context, Project) error { return nil }
|
|
func (*repositoryStub) CreateEnvironment(context.Context, Environment) error { return nil }
|
|
func (*repositoryStub) CreateApplicationService(context.Context, ApplicationService) error {
|
|
return nil
|
|
}
|
|
func (repository *repositoryStub) CreateInvitation(_ context.Context, invitation Invitation) error {
|
|
repository.invitation = invitation
|
|
return nil
|
|
}
|
|
func (repository *repositoryStub) InvitationByDigest(context.Context, [32]byte, time.Time) (Invitation, error) {
|
|
if repository.invitationErr != nil {
|
|
return Invitation{}, repository.invitationErr
|
|
}
|
|
return repository.invitation, nil
|
|
}
|
|
func (repository *repositoryStub) AcceptInvitation(_ context.Context, _ [32]byte, userID string, _ time.Time) error {
|
|
repository.acceptedUser = userID
|
|
return nil
|
|
}
|
|
func (*repositoryStub) MembershipsForUser(context.Context, string) ([]Membership, error) {
|
|
return nil, nil
|
|
}
|
|
func (*repositoryStub) TeamsForUser(context.Context, string, string) ([]Team, error) { return nil, nil }
|