refactor: 移除 Database registry 与 Instance 初始化依赖
This commit is contained in:
@@ -32,11 +32,9 @@ func completeChecks() instance.ManagementChecks {
|
||||
}
|
||||
}
|
||||
|
||||
func capability(t *testing.T, value *instance.Instance, checks instance.ManagementChecks,
|
||||
registry instance.RegistryState,
|
||||
) instance.CapabilityObservation {
|
||||
func capability(t *testing.T, value *instance.Instance, checks instance.ManagementChecks) instance.CapabilityObservation {
|
||||
t.Helper()
|
||||
o, err := instance.NewCapabilityObservation(value.Target(), testServerVersion, checks, registry)
|
||||
o, err := instance.NewCapabilityObservation(value.Target(), testServerVersion, checks)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -45,8 +43,8 @@ func capability(t *testing.T, value *instance.Instance, checks instance.Manageme
|
||||
|
||||
func readyInstance(t *testing.T) *instance.Instance {
|
||||
t.Helper()
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
|
||||
if err := i.AssessRegistryResult(instance.RegistryReadBack(capability(t, i, completeChecks(), instance.RegistryUsable))); err != nil {
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseValidating}, false)
|
||||
if err := i.AssessManagement(capability(t, i, completeChecks())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.RequireProvisioningReady(); err != nil {
|
||||
@@ -60,38 +58,27 @@ func TestReadinessRequiresCompleteReadBack(t *testing.T) {
|
||||
if err := i.BeginValidation(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
absent := capability(t, i, completeChecks(), instance.RegistryAbsent)
|
||||
if err := i.AssessManagement(absent); err != nil {
|
||||
if i.RequireProvisioningReady() == nil {
|
||||
t.Fatal("validation intent authorized provisioning")
|
||||
}
|
||||
if err := i.AssessManagement(capability(t, i, instance.ManagementChecks{})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := i.Snapshot(); s.Phase != instance.PhaseInitializingRegistry || s.ObservedRevision != 0 || s.Readiness != instance.Unknown {
|
||||
t.Fatalf("management observation prematurely concluded readiness: %+v", s)
|
||||
if snapshot := i.Snapshot(); snapshot.Phase != instance.PhaseValidating ||
|
||||
snapshot.Readiness != instance.NotReady || snapshot.Failure != instance.ObservationIncomplete {
|
||||
t.Fatalf("incomplete observation accepted: %+v", snapshot)
|
||||
}
|
||||
for range 2 {
|
||||
decision, err := i.PlanRegistryPreparation(absent)
|
||||
if err != nil || decision != instance.PreparationAllowed {
|
||||
t.Fatalf("preparation: %v, %v", decision, err)
|
||||
}
|
||||
if i.RequireProvisioningReady() == nil {
|
||||
t.Fatal("preparation authorized provisioning")
|
||||
}
|
||||
if i.RequireProvisioningReady() == nil {
|
||||
t.Fatal("incomplete observation authorized provisioning")
|
||||
}
|
||||
if err := i.AssessRegistryResult(instance.RegistryReadBack(absent)); err != nil {
|
||||
if err := i.AssessManagement(capability(t, i, completeChecks())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i.Snapshot().Failure != instance.RegistryNotUsable || i.RequireProvisioningReady() == nil {
|
||||
t.Fatal("absent registry accepted as ready")
|
||||
}
|
||||
usable := capability(t, i, completeChecks(), instance.RegistryUsable)
|
||||
decision, err := i.PlanRegistryPreparation(usable)
|
||||
if err != nil || decision != instance.AlreadyUsable {
|
||||
t.Fatalf("retry after external preparation: %v, %v", decision, err)
|
||||
}
|
||||
if err := i.AssessRegistryResult(instance.RegistryReadBack(usable)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := i.Snapshot(); s.Readiness != instance.Ready || s.ReportedVersion != testServerVersion || s.ObservedRevision != i.Target().Revision().Value() {
|
||||
t.Fatalf("complete observation not accepted: %+v", s)
|
||||
snapshot := i.Snapshot()
|
||||
if snapshot.Phase != instance.PhaseReady || snapshot.Readiness != instance.Ready ||
|
||||
snapshot.ReportedVersion != testServerVersion ||
|
||||
snapshot.ObservedRevision != i.Target().Revision().Value() {
|
||||
t.Fatalf("complete management observation did not establish readiness: %+v", snapshot)
|
||||
}
|
||||
if err := i.RequireProvisioningReady(); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -109,7 +96,7 @@ func TestReadinessRecoveryAndInvalidation(t *testing.T) {
|
||||
t.Fatal("persisted Ready fabricated fresh evidence")
|
||||
}
|
||||
for range 2 {
|
||||
if err := restored.AssessReadiness(capability(t, restored, completeChecks(), instance.RegistryUsable)); err != nil {
|
||||
if err := restored.AssessReadiness(capability(t, restored, completeChecks())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := restored.RequireProvisioningReady(); err != nil {
|
||||
@@ -138,120 +125,77 @@ func TestReadinessRecoveryAndInvalidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEachManagementCheckIsRequired(t *testing.T) {
|
||||
for field := range 6 {
|
||||
for _, result := range []instance.CheckResult{instance.CheckUnobserved, instance.CheckUnavailable,
|
||||
instance.CheckAuthenticationFailed, instance.CheckInsufficientPrivileges, 255} {
|
||||
checks := completeChecks()
|
||||
fields := []*instance.CheckResult{&checks.Connection, &checks.Metadata, &checks.Roles,
|
||||
&checks.Databases, &checks.Grants, &checks.Extensions}
|
||||
*fields[field] = result
|
||||
i := readyInstance(t)
|
||||
if err := i.AssessReadiness(capability(t, i, checks, instance.RegistryUsable)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := i.Snapshot(); s.Phase != instance.PhaseValidating || s.Readiness != instance.NotReady ||
|
||||
s.Failure == instance.NoFailure || i.RequireProvisioningReady() == nil {
|
||||
t.Fatalf("check %d result %d accepted: %+v", field, result, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryDecisionsAndReadinessLoss(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
state instance.RegistryState
|
||||
decision instance.PreparationDecision
|
||||
failure instance.Failure
|
||||
checkNames := []string{"connection", "metadata", "roles", "databases", "grants", "extensions"}
|
||||
failures := []struct {
|
||||
name string
|
||||
result instance.CheckResult
|
||||
want instance.Failure
|
||||
}{
|
||||
{instance.RegistryUsable, instance.AlreadyUsable, instance.NoFailure},
|
||||
{instance.RegistryAbsent, instance.PreparationAllowed, instance.RegistryNotUsable},
|
||||
{instance.RegistryNeedsMigration, instance.PreparationAllowed, instance.RegistryNotUsable},
|
||||
{instance.RegistryUnsupported, instance.PreparationDenied, instance.RegistryIncompatible},
|
||||
{instance.RegistryUnavailable, instance.PreparationDenied, instance.DependencyUnavailable},
|
||||
{instance.RegistryUnobserved, instance.PreparationDenied, instance.ObservationIncomplete},
|
||||
{255, instance.PreparationDenied, instance.ObservationIncomplete},
|
||||
} {
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
|
||||
o := capability(t, i, completeChecks(), tc.state)
|
||||
decision, err := i.PlanRegistryPreparation(o)
|
||||
if err != nil || decision != tc.decision {
|
||||
t.Fatalf("registry %d: %v, %v", tc.state, decision, err)
|
||||
}
|
||||
i = readyInstance(t)
|
||||
if err := i.AssessReadiness(o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i.Snapshot().Failure != tc.failure {
|
||||
t.Fatalf("registry %d: %+v", tc.state, i.Snapshot())
|
||||
}
|
||||
if tc.state != instance.RegistryUsable {
|
||||
wantPhase := instance.PhaseInitializingRegistry
|
||||
if tc.state == instance.RegistryUnavailable {
|
||||
wantPhase = instance.PhaseValidating
|
||||
}
|
||||
if i.Snapshot().Phase != wantPhase || i.RequireProvisioningReady() == nil {
|
||||
t.Fatal("registry drift retained readiness")
|
||||
{"unobserved", instance.CheckUnobserved, instance.ObservationIncomplete},
|
||||
{"unavailable", instance.CheckUnavailable, instance.DependencyUnavailable},
|
||||
{"authentication", instance.CheckAuthenticationFailed, instance.AuthenticationFailed},
|
||||
{"privileges", instance.CheckInsufficientPrivileges, instance.InsufficientPrivileges},
|
||||
{"unknown", 255, instance.ObservationIncomplete},
|
||||
}
|
||||
for field, name := range checkNames {
|
||||
for _, failure := range failures {
|
||||
for _, phase := range []instance.Phase{instance.PhaseValidating, instance.PhaseReady} {
|
||||
t.Run(name+"/"+failure.name+"/"+string(phase), func(t *testing.T) {
|
||||
checks := completeChecks()
|
||||
fields := []*instance.CheckResult{
|
||||
&checks.Connection, &checks.Metadata, &checks.Roles,
|
||||
&checks.Databases, &checks.Grants, &checks.Extensions,
|
||||
}
|
||||
*fields[field] = failure.result
|
||||
value := lifecycleInstance(t, instance.Snapshot{Phase: phase}, false)
|
||||
assess := value.AssessManagement
|
||||
if phase == instance.PhaseReady {
|
||||
value = readyInstance(t)
|
||||
assess = value.AssessReadiness
|
||||
}
|
||||
if err := assess(capability(t, value, checks)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snapshot := value.Snapshot()
|
||||
if snapshot.Phase != instance.PhaseValidating ||
|
||||
snapshot.Readiness != instance.NotReady ||
|
||||
snapshot.Failure != failure.want ||
|
||||
snapshot.ObservedRevision != value.Target().Revision().Value() {
|
||||
t.Fatalf("incorrect failed observation: %+v", snapshot)
|
||||
}
|
||||
if value.RequireProvisioningReady() == nil {
|
||||
t.Fatal("failed check authorized provisioning")
|
||||
}
|
||||
// 依赖恢复后重新验证,不保留失败或旧就绪证据。
|
||||
if err := value.AssessManagement(capability(t, value, completeChecks())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := value.RequireProvisioningReady(); err != nil {
|
||||
t.Fatal("dependency recovery did not restore readiness", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitializationRejectsIncompleteOrFailedManagement(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
checks instance.ManagementChecks
|
||||
registry instance.RegistryState
|
||||
failure instance.Failure
|
||||
}{
|
||||
{instance.ManagementChecks{}, instance.RegistryUsable, instance.ObservationIncomplete},
|
||||
{completeChecks(), instance.RegistryUnsupported, instance.RegistryIncompatible},
|
||||
{completeChecks(), instance.RegistryUnavailable, instance.DependencyUnavailable},
|
||||
} {
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseValidating}, false)
|
||||
if err := i.AssessManagement(capability(t, i, tc.checks, tc.registry)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := i.Snapshot(); s.Phase != instance.PhaseValidating || s.Failure != tc.failure ||
|
||||
s.ObservedRevision != i.Target().Revision().Value() || s.Readiness != instance.NotReady {
|
||||
t.Fatalf("invalid management accepted: %+v", s)
|
||||
}
|
||||
}
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
|
||||
decision, err := i.PlanRegistryPreparation(capability(t, i, instance.ManagementChecks{}, instance.RegistryAbsent))
|
||||
if err != nil || decision != instance.PreparationDenied || i.Snapshot().Failure != instance.ObservationIncomplete {
|
||||
t.Fatal("incomplete management allowed registry writes")
|
||||
}
|
||||
if err := i.AssessRegistryResult(instance.RegistryReadBack(capability(t, i, completeChecks(), instance.RegistryUsable))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.RequireProvisioningReady(); err != nil {
|
||||
t.Fatal("dependency recovery did not restore readiness", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadinessMethodsRejectWrongPhaseAndDeletion(t *testing.T) {
|
||||
for _, deleting := range []bool{false, true} {
|
||||
for _, phase := range []instance.Phase{instance.PhasePending, instance.PhaseValidating,
|
||||
instance.PhaseInitializingRegistry, instance.PhaseReady, instance.PhaseDeleting} {
|
||||
instance.PhaseReady, instance.PhaseDeleting} {
|
||||
for _, operation := range []struct {
|
||||
phase instance.Phase
|
||||
apply func(*instance.Instance, instance.CapabilityObservation) error
|
||||
}{
|
||||
{instance.PhaseValidating, (*instance.Instance).AssessManagement},
|
||||
{instance.PhaseReady, (*instance.Instance).AssessReadiness},
|
||||
{instance.PhaseInitializingRegistry, func(i *instance.Instance, o instance.CapabilityObservation) error {
|
||||
_, err := i.PlanRegistryPreparation(o)
|
||||
return err
|
||||
}},
|
||||
{instance.PhaseInitializingRegistry, func(i *instance.Instance, o instance.CapabilityObservation) error {
|
||||
return i.AssessRegistryResult(instance.RegistryReadBack(o))
|
||||
}},
|
||||
} {
|
||||
if !deleting && operation.phase == phase {
|
||||
continue
|
||||
}
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: phase}, deleting)
|
||||
before := i.Snapshot()
|
||||
if err := operation.apply(i, capability(t, i, completeChecks(), instance.RegistryUsable)); err == nil {
|
||||
if err := operation.apply(i, capability(t, i, completeChecks())); err == nil {
|
||||
t.Fatalf("phase %s deleting=%t accepted operation for %s", phase, deleting, operation.phase)
|
||||
}
|
||||
if i.Snapshot() != before {
|
||||
@@ -273,7 +217,7 @@ func TestOldGenerationObservationDoesNotReplaceEvidence(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
o, err := instance.NewCapabilityObservation(other, testServerVersion, completeChecks(), instance.RegistryUsable)
|
||||
o, err := instance.NewCapabilityObservation(other, testServerVersion, completeChecks())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -286,44 +230,14 @@ func TestOldGenerationObservationDoesNotReplaceEvidence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparationFailureCannotEstablishReadiness(t *testing.T) {
|
||||
i := lifecycleInstance(t, instance.Snapshot{Phase: instance.PhaseInitializingRegistry}, false)
|
||||
for _, failure := range []instance.Failure{instance.DependencyUnavailable, instance.AuthenticationFailed,
|
||||
instance.InsufficientPrivileges, instance.RegistryIncompatible} {
|
||||
result, err := instance.RegistryPreparationFailed(i.Target(), failure)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := i.AssessRegistryResult(result); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := i.Snapshot(); s.Failure != failure || s.Readiness != instance.NotReady ||
|
||||
s.Phase != instance.PhaseInitializingRegistry || i.RequireProvisioningReady() == nil {
|
||||
t.Fatalf("failed operation accepted: %+v", s)
|
||||
}
|
||||
}
|
||||
for _, failure := range []instance.Failure{instance.NoFailure, 255} {
|
||||
if _, err := instance.RegistryPreparationFailed(i.Target(), failure); err == nil {
|
||||
t.Fatal("invalid failure accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapabilityInputsAndLifecycleGuards(t *testing.T) {
|
||||
i := readyInstance(t)
|
||||
if _, err := instance.NewCapabilityObservation(instance.ObservationTarget{}, testServerVersion,
|
||||
completeChecks(), instance.RegistryUsable); err == nil {
|
||||
completeChecks()); err == nil {
|
||||
t.Fatal("invalid target accepted")
|
||||
}
|
||||
if _, err := instance.RegistryPreparationFailed(instance.ObservationTarget{}, instance.DependencyUnavailable); err == nil {
|
||||
t.Fatal("invalid failure target accepted")
|
||||
}
|
||||
for _, method := range []func(instance.CapabilityObservation) error{
|
||||
i.AssessManagement, i.AssessReadiness,
|
||||
func(o instance.CapabilityObservation) error { _, err := i.PlanRegistryPreparation(o); return err },
|
||||
func(o instance.CapabilityObservation) error {
|
||||
return i.AssessRegistryResult(instance.RegistryReadBack(o))
|
||||
},
|
||||
} {
|
||||
before := i.Snapshot()
|
||||
if err := method(instance.CapabilityObservation{}); err == nil || i.Snapshot() != before {
|
||||
@@ -333,13 +247,13 @@ func TestCapabilityInputsAndLifecycleGuards(t *testing.T) {
|
||||
old := i.Snapshot()
|
||||
old.ObservedRevision = 0
|
||||
changed := lifecycleInstance(t, old, false)
|
||||
if err := changed.AssessReadiness(capability(t, changed, completeChecks(), instance.RegistryUsable)); err != nil {
|
||||
if err := changed.AssessReadiness(capability(t, changed, completeChecks())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s := changed.Snapshot(); s.Phase != instance.PhaseValidating || s.ObservedRevision != 0 || s.Readiness != instance.Unknown {
|
||||
t.Fatalf("changed generation accepted old checkpoint: %+v", s)
|
||||
}
|
||||
o, err := instance.NewCapabilityObservation(i.Target(), "", completeChecks(), instance.RegistryUsable)
|
||||
o, err := instance.NewCapabilityObservation(i.Target(), "", completeChecks())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user