- migration writer now emits unique constraints (direct apply and live diff previously dropped them); idempotent via the existing DO block - unique constraint adopts a same-named model unique index with ADD CONSTRAINT ... UNIQUE USING INDEX instead of building a second index - dbml reader names unnamed unique indexes ukey_<table>_<cols> to match the unique constraint naming - foreign key deferral: Constraint.Deferrable/InitiallyDeferred are *bool (nil = unspecified, never diffed); writers emit DEFERRABLE, DEFERRABLE INITIALLY DEFERRED and NOT DEFERRABLE; live reader reads condeferrable and condeferred; dbml reader/writer support deferrable and initially deferred - tests and README/naming docs updated
60 lines
2.7 KiB
Go
60 lines
2.7 KiB
Go
package models
|
|
|
|
import "testing"
|
|
|
|
func boolPtr(b bool) *bool { return &b }
|
|
|
|
func TestConstraint_DeferralClause(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
c Constraint
|
|
want string
|
|
}{
|
|
{"unspecified", Constraint{}, ""},
|
|
{"deferrable", Constraint{Deferrable: boolPtr(true)}, "DEFERRABLE"},
|
|
{"deferrable initially deferred", Constraint{Deferrable: boolPtr(true), InitiallyDeferred: boolPtr(true)}, "DEFERRABLE INITIALLY DEFERRED"},
|
|
{"deferrable initially immediate", Constraint{Deferrable: boolPtr(true), InitiallyDeferred: boolPtr(false)}, "DEFERRABLE"},
|
|
{"initially deferred implies deferrable", Constraint{InitiallyDeferred: boolPtr(true)}, "DEFERRABLE INITIALLY DEFERRED"},
|
|
{"initially immediate alone is unspecified", Constraint{InitiallyDeferred: boolPtr(false)}, ""},
|
|
{"explicitly not deferrable", Constraint{Deferrable: boolPtr(false)}, "NOT DEFERRABLE"},
|
|
{"not deferrable ignores initially deferred", Constraint{Deferrable: boolPtr(false), InitiallyDeferred: boolPtr(true)}, "NOT DEFERRABLE"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.c.DeferralClause(); got != tt.want {
|
|
t.Errorf("DeferralClause() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeferralMatches(t *testing.T) {
|
|
live := func(def, init bool) *Constraint {
|
|
return &Constraint{Deferrable: boolPtr(def), InitiallyDeferred: boolPtr(init)}
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
model, other *Constraint
|
|
want bool
|
|
}{
|
|
{"model unspecified vs deferrable live", &Constraint{}, live(true, false), true},
|
|
{"model unspecified vs not deferrable live", &Constraint{}, live(false, false), true},
|
|
{"deferrable vs deferrable live", &Constraint{Deferrable: boolPtr(true)}, live(true, true), true},
|
|
{"deferrable vs not deferrable live", &Constraint{Deferrable: boolPtr(true)}, live(false, false), false},
|
|
{"not deferrable vs deferrable live", &Constraint{Deferrable: boolPtr(false)}, live(true, false), false},
|
|
{"initially deferred vs initially immediate live", &Constraint{InitiallyDeferred: boolPtr(true)}, live(true, false), false},
|
|
{"initially deferred vs initially deferred live", &Constraint{InitiallyDeferred: boolPtr(true)}, live(true, true), true},
|
|
{"explicit initially immediate vs initially deferred live", &Constraint{Deferrable: boolPtr(true), InitiallyDeferred: boolPtr(false)}, live(true, true), false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := DeferralMatches(tt.model, tt.other); got != tt.want {
|
|
t.Errorf("DeferralMatches() = %v, want %v", got, tt.want)
|
|
}
|
|
if got := DeferralMatches(tt.other, tt.model); got != tt.want {
|
|
t.Errorf("DeferralMatches() reversed = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|