feat: authsession service
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// Package sessionlimit defines the domain decision shape used for active
|
||||
// device-session limit evaluation.
|
||||
package sessionlimit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Kind identifies the coarse outcome of evaluating the active-session limit.
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
// KindDisabled reports that no configured limit is currently active.
|
||||
KindDisabled Kind = "disabled"
|
||||
|
||||
// KindAllowed reports that creating the next session is allowed.
|
||||
KindAllowed Kind = "allowed"
|
||||
|
||||
// KindExceeded reports that creating the next session would exceed the
|
||||
// configured limit.
|
||||
KindExceeded Kind = "exceeded"
|
||||
)
|
||||
|
||||
// IsKnown reports whether Kind is one of the session-limit outcomes supported
|
||||
// by the current domain model.
|
||||
func (k Kind) IsKnown() bool {
|
||||
switch k {
|
||||
case KindDisabled, KindAllowed, KindExceeded:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Decision stores the result of evaluating one possible next session creation.
|
||||
type Decision struct {
|
||||
// Kind reports the coarse decision outcome.
|
||||
Kind Kind
|
||||
|
||||
// ConfiguredLimit stores the active configured limit when one exists.
|
||||
ConfiguredLimit *int
|
||||
|
||||
// ActiveSessionCount stores the current active-session count before create.
|
||||
ActiveSessionCount int
|
||||
|
||||
// NextSessionCount stores the count that would exist after creating the next
|
||||
// session.
|
||||
NextSessionCount int
|
||||
}
|
||||
|
||||
// Validate reports whether Decision satisfies the Stage-2 structural
|
||||
// invariants.
|
||||
func (d Decision) Validate() error {
|
||||
if !d.Kind.IsKnown() {
|
||||
return fmt.Errorf("session-limit decision kind %q is unsupported", d.Kind)
|
||||
}
|
||||
if d.ActiveSessionCount < 0 {
|
||||
return errors.New("session-limit active session count must not be negative")
|
||||
}
|
||||
if d.NextSessionCount < 0 {
|
||||
return errors.New("session-limit next session count must not be negative")
|
||||
}
|
||||
if d.NextSessionCount != d.ActiveSessionCount+1 {
|
||||
return errors.New("session-limit next session count must equal active session count plus one")
|
||||
}
|
||||
|
||||
switch d.Kind {
|
||||
case KindDisabled:
|
||||
if d.ConfiguredLimit != nil {
|
||||
return errors.New("disabled session-limit decision must not contain configured limit")
|
||||
}
|
||||
case KindAllowed, KindExceeded:
|
||||
if d.ConfiguredLimit == nil {
|
||||
return errors.New("limited session-limit decision must contain configured limit")
|
||||
}
|
||||
if *d.ConfiguredLimit <= 0 {
|
||||
return errors.New("session-limit configured limit must be positive")
|
||||
}
|
||||
if d.Kind == KindAllowed && d.NextSessionCount > *d.ConfiguredLimit {
|
||||
return errors.New("allowed session-limit decision must not exceed configured limit")
|
||||
}
|
||||
if d.Kind == KindExceeded && d.NextSessionCount <= *d.ConfiguredLimit {
|
||||
return errors.New("exceeded session-limit decision must be above configured limit")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package sessionlimit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestKindIsKnown(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value Kind
|
||||
want bool
|
||||
}{
|
||||
{name: "disabled", value: KindDisabled, want: true},
|
||||
{name: "allowed", value: KindAllowed, want: true},
|
||||
{name: "exceeded", value: KindExceeded, want: true},
|
||||
{name: "unknown", value: Kind("unknown"), want: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tt.value.IsKnown(); got != tt.want {
|
||||
require.Failf(t, "test failed", "IsKnown() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecisionValidate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
limitTwo := 2
|
||||
limitThree := 3
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value Decision
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "disabled valid",
|
||||
value: Decision{
|
||||
Kind: KindDisabled,
|
||||
ActiveSessionCount: 0,
|
||||
NextSessionCount: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "allowed valid",
|
||||
value: Decision{
|
||||
Kind: KindAllowed,
|
||||
ConfiguredLimit: &limitThree,
|
||||
ActiveSessionCount: 1,
|
||||
NextSessionCount: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exceeded valid",
|
||||
value: Decision{
|
||||
Kind: KindExceeded,
|
||||
ConfiguredLimit: &limitTwo,
|
||||
ActiveSessionCount: 2,
|
||||
NextSessionCount: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "disabled rejects limit",
|
||||
value: Decision{
|
||||
Kind: KindDisabled,
|
||||
ConfiguredLimit: &limitTwo,
|
||||
ActiveSessionCount: 0,
|
||||
NextSessionCount: 1,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "allowed requires limit",
|
||||
value: Decision{
|
||||
Kind: KindAllowed,
|
||||
ActiveSessionCount: 0,
|
||||
NextSessionCount: 1,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "allowed rejects overflow",
|
||||
value: Decision{
|
||||
Kind: KindAllowed,
|
||||
ConfiguredLimit: &limitTwo,
|
||||
ActiveSessionCount: 2,
|
||||
NextSessionCount: 3,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "next count must be active plus one",
|
||||
value: Decision{
|
||||
Kind: KindDisabled,
|
||||
ActiveSessionCount: 2,
|
||||
NextSessionCount: 2,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := tt.value.Validate()
|
||||
if tt.wantErr && err == nil {
|
||||
require.FailNow(t, "Validate() returned nil error")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
require.Failf(t, "test failed", "Validate() returned error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user