f-21: test: add test for agent dispatcher
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
712692d414
commit
ba2f8080be
4 changed files with 325 additions and 0 deletions
63
internal/dispatcher/agent/dispatcher_test.go
Normal file
63
internal/dispatcher/agent/dispatcher_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
||||||
|
"github.com/dgraph-io/badger/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDispatcher_Prepare_Success(t *testing.T) {
|
||||||
|
d, _ := newTestDispatcher(t)
|
||||||
|
cmd := mockCmd{
|
||||||
|
prepareFn: func(*badger.DB, *configuration.Config) error { return nil },
|
||||||
|
executeFn: func(*badger.DB, *configuration.Config) error { return nil },
|
||||||
|
}
|
||||||
|
if err := d.Prepare(cmd); err != nil {
|
||||||
|
t.Errorf("Prepare devrait retourner nil, obtenu : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDispatcher_Prepare_PropagatesError(t *testing.T) {
|
||||||
|
d, _ := newTestDispatcher(t)
|
||||||
|
want := errors.New("prepare failed")
|
||||||
|
cmd := mockCmd{
|
||||||
|
prepareFn: func(*badger.DB, *configuration.Config) error { return want },
|
||||||
|
executeFn: func(*badger.DB, *configuration.Config) error { return nil },
|
||||||
|
}
|
||||||
|
if err := d.Prepare(cmd); !errors.Is(err, want) {
|
||||||
|
t.Errorf("attendu %v, obtenu %v", want, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDispatcher_Dispatch_ExecutesCommand(t *testing.T) {
|
||||||
|
d, _ := newTestDispatcher(t)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
cmd := mockCmd{
|
||||||
|
prepareFn: func(*badger.DB, *configuration.Config) error { return nil },
|
||||||
|
executeFn: func(*badger.DB, *configuration.Config) error {
|
||||||
|
wg.Done()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
d.Dispatch(cmd)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDispatcher_Dispatch_ExecuteErrorLogged(t *testing.T) {
|
||||||
|
d, _ := newTestDispatcher(t)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
cmd := mockCmd{
|
||||||
|
prepareFn: func(*badger.DB, *configuration.Config) error { return nil },
|
||||||
|
executeFn: func(*badger.DB, *configuration.Config) error {
|
||||||
|
defer wg.Done()
|
||||||
|
return errors.New("execute failed")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
d.Dispatch(cmd)
|
||||||
|
wg.Wait() // Execute s'est terminé — l'erreur est loggée, pas propagée
|
||||||
|
}
|
||||||
38
internal/dispatcher/agent/helpers_test.go
Normal file
38
internal/dispatcher/agent/helpers_test.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
||||||
|
"git.g3e.fr/syonad/two/pkg/db/kv"
|
||||||
|
"git.g3e.fr/syonad/two/pkg/worker"
|
||||||
|
"github.com/dgraph-io/badger/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestDispatcher(t *testing.T) (*Dispatcher, *badger.DB) {
|
||||||
|
t.Helper()
|
||||||
|
db := kv.InitDB(kv.Config{Path: t.TempDir()}, false)
|
||||||
|
t.Cleanup(func() { db.Close() })
|
||||||
|
q := worker.New(100)
|
||||||
|
q.Start(2)
|
||||||
|
cfg := &configuration.Config{DefaultInterface: "br-default"}
|
||||||
|
cfg.Interfaces = map[string]string{"vms": "br-vms"}
|
||||||
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||||
|
return New(q, db, cfg, logger), db
|
||||||
|
}
|
||||||
|
|
||||||
|
// mockCmd implémente Command sans aucune dépendance système.
|
||||||
|
type mockCmd struct {
|
||||||
|
prepareFn func(*badger.DB, *configuration.Config) error
|
||||||
|
executeFn func(*badger.DB, *configuration.Config) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockCmd) Prepare(db *badger.DB, cfg *configuration.Config) error {
|
||||||
|
return m.prepareFn(db, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockCmd) Execute(db *badger.DB, cfg *configuration.Config) error {
|
||||||
|
return m.executeFn(db, cfg)
|
||||||
|
}
|
||||||
135
internal/dispatcher/agent/subnet_commands_test.go
Normal file
135
internal/dispatcher/agent/subnet_commands_test.go
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
||||||
|
"git.g3e.fr/syonad/two/pkg/db/kv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testCfg() *configuration.Config {
|
||||||
|
cfg := &configuration.Config{DefaultInterface: "br-default"}
|
||||||
|
cfg.Interfaces = map[string]string{"vms": "br-vms"}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CreateSubnetCommand.Prepare ---
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_Success(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-1/state", "created")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-1", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, testCfg()); err != nil {
|
||||||
|
t.Fatalf("Prepare a échoué : %v", err)
|
||||||
|
}
|
||||||
|
state, _ := kv.GetFromDB(db, "subnet/sn-1/state")
|
||||||
|
if state != "creating" {
|
||||||
|
t.Errorf("state attendu creating, obtenu %q", state)
|
||||||
|
}
|
||||||
|
vpc, _ := kv.GetFromDB(db, "subnet/sn-1/vpc")
|
||||||
|
if vpc != "vpc-1" {
|
||||||
|
t.Errorf("vpc attendu vpc-1, obtenu %q", vpc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_UsesIfaceTypeMapping(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-1/state", "created")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-1", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
cmd.Prepare(db, testCfg())
|
||||||
|
iface, _ := kv.GetFromDB(db, "subnet/sn-1/local_iface")
|
||||||
|
if iface != "br-vms" {
|
||||||
|
t.Errorf("local_iface attendu br-vms, obtenu %q", iface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_UsesDefaultIfaceWhenTypeUnknown(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-1/state", "created")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-1", VxlanID: 100,
|
||||||
|
IfaceType: "inconnu", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
cmd.Prepare(db, testCfg())
|
||||||
|
iface, _ := kv.GetFromDB(db, "subnet/sn-1/local_iface")
|
||||||
|
if iface != "br-default" {
|
||||||
|
t.Errorf("local_iface attendu br-default, obtenu %q", iface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_Duplicate(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-1/state", "created")
|
||||||
|
kv.AddInDB(db, "subnet/sn-exist/state", "created")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-exist", VPC: "vpc-1", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, testCfg()); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer sur un subnet déjà existant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_VPCNotFound(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-inexistant", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, testCfg()); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si le VPC n'existe pas")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_VPCDeleting(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-dying/state", "deleting")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-dying", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, testCfg()); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si le VPC est en cours de suppression")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSubnetCommand_Prepare_VPCDeleted(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-gone/state", "deleted")
|
||||||
|
cmd := CreateSubnetCommand{
|
||||||
|
Name: "sn-1", VPC: "vpc-gone", VxlanID: 100,
|
||||||
|
IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24",
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, testCfg()); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si le VPC est supprimé")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DeleteSubnetCommand.Prepare ---
|
||||||
|
|
||||||
|
func TestDeleteSubnetCommand_Prepare_Success(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "subnet/sn-del/state", "created")
|
||||||
|
cmd := DeleteSubnetCommand{Name: "sn-del"}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare a échoué : %v", err)
|
||||||
|
}
|
||||||
|
state, _ := kv.GetFromDB(db, "subnet/sn-del/state")
|
||||||
|
if state != "deleting" {
|
||||||
|
t.Errorf("state attendu deleting, obtenu %q", state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteSubnetCommand_Prepare_NotFound(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
cmd := DeleteSubnetCommand{Name: "sn-inexistant"}
|
||||||
|
if err := cmd.Prepare(db, nil); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si le subnet n'existe pas")
|
||||||
|
}
|
||||||
|
}
|
||||||
89
internal/dispatcher/agent/vpc_commands_test.go
Normal file
89
internal/dispatcher/agent/vpc_commands_test.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.g3e.fr/syonad/two/pkg/db/kv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- CreateVPCCommand.Prepare ---
|
||||||
|
|
||||||
|
func TestCreateVPCCommand_Prepare_NewVPC(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
cmd := CreateVPCCommand{Name: "vpc-1"}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare a échoué : %v", err)
|
||||||
|
}
|
||||||
|
state, err := kv.GetFromDB(db, "vpc/vpc-1/state")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("état non écrit en DB : %v", err)
|
||||||
|
}
|
||||||
|
if state != "creating" {
|
||||||
|
t.Errorf("state attendu creating, obtenu %q", state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateVPCCommand_Prepare_Duplicate(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-exist/state", "created")
|
||||||
|
cmd := CreateVPCCommand{Name: "vpc-exist"}
|
||||||
|
if err := cmd.Prepare(db, nil); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer sur un VPC déjà existant")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DeleteVPCCommand.Prepare ---
|
||||||
|
|
||||||
|
func TestDeleteVPCCommand_Prepare_Success(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-del/state", "created")
|
||||||
|
cmd := DeleteVPCCommand{Name: "vpc-del"}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare a échoué : %v", err)
|
||||||
|
}
|
||||||
|
state, _ := kv.GetFromDB(db, "vpc/vpc-del/state")
|
||||||
|
if state != "deleting" {
|
||||||
|
t.Errorf("state attendu deleting, obtenu %q", state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteVPCCommand_Prepare_NotFound(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
cmd := DeleteVPCCommand{Name: "vpc-inexistant"}
|
||||||
|
if err := cmd.Prepare(db, nil); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si le VPC n'existe pas")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteVPCCommand_Prepare_BlockedByActiveSubnet(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-busy/state", "created")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/state", "created")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/vpc", "vpc-busy")
|
||||||
|
cmd := DeleteVPCCommand{Name: "vpc-busy"}
|
||||||
|
if err := cmd.Prepare(db, nil); err == nil {
|
||||||
|
t.Error("Prepare devrait échouer si un subnet actif existe")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteVPCCommand_Prepare_AllowedWhenSubnetDeleted(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-ok/state", "created")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/state", "deleted")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/vpc", "vpc-ok")
|
||||||
|
cmd := DeleteVPCCommand{Name: "vpc-ok"}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare devrait réussir si le subnet est deleted : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteVPCCommand_Prepare_AllowedWhenSubnetDeleting(t *testing.T) {
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "vpc/vpc-ok/state", "created")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/state", "deleting")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/vpc", "vpc-ok")
|
||||||
|
cmd := DeleteVPCCommand{Name: "vpc-ok"}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare devrait réussir si le subnet est deleting : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue