This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package saas
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"data-terminal-cloud/internal/audit"
|
||||
"data-terminal-cloud/internal/identity"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func TestDeviceBindingImpactTransferAndUnbind(t *testing.T) {
|
||||
databaseURL := os.Getenv("TEST_DATABASE_URL")
|
||||
if databaseURL == "" {
|
||||
t.Skip("TEST_DATABASE_URL is not set")
|
||||
}
|
||||
ctx := context.Background()
|
||||
pool, err := pgxpool.New(ctx, databaseURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pool.Close()
|
||||
|
||||
devicePublicID := uuid.NewString()
|
||||
targetSubjectPublicID := uuid.NewString()
|
||||
serialSum := sha256.Sum256([]byte(devicePublicID))
|
||||
serialDigest := hex.EncodeToString(serialSum[:])
|
||||
var deviceID, targetSubjectID string
|
||||
if err := pool.QueryRow(ctx, `
|
||||
INSERT INTO subject_profile (public_id,tenant_id,household_id,preferred_name,status,profile_version)
|
||||
SELECT $1::uuid,t.id,h.id,'绑定转移测试长者','active',1
|
||||
FROM tenant t JOIN household h ON h.tenant_id=t.id
|
||||
WHERE t.public_id=$2::uuid AND h.public_id=$3::uuid
|
||||
RETURNING id::text`, targetSubjectPublicID, testTenantPublicID, "10000000-0000-0000-0000-000000000211").Scan(&targetSubjectID); err != nil {
|
||||
t.Fatalf("insert target subject: %v", err)
|
||||
}
|
||||
if err := pool.QueryRow(ctx, `
|
||||
INSERT INTO device (public_id,tenant_id,serial_digest,model,hardware_revision,status,credential_ref,minimum_security_version)
|
||||
SELECT $1::uuid,id,$2,'binding-test-terminal','test-rev','registered','db://binding-test','test-v1'
|
||||
FROM tenant WHERE public_id=$3::uuid
|
||||
RETURNING id::text`, devicePublicID, serialDigest, testTenantPublicID).Scan(&deviceID); err != nil {
|
||||
t.Fatalf("insert device: %v", err)
|
||||
}
|
||||
if _, err := pool.Exec(ctx, `INSERT INTO device_twin (device_id,tenant_id) SELECT id,tenant_id FROM device WHERE id=$1::uuid`, deviceID); err != nil {
|
||||
t.Fatalf("insert device twin: %v", err)
|
||||
}
|
||||
var sourceBindingID string
|
||||
if err := pool.QueryRow(ctx, `
|
||||
INSERT INTO device_binding (tenant_id,device_id,household_id,subject_id,bound_by,reason)
|
||||
SELECT d.tenant_id,d.id,h.id,sp.id,ua.id,'integration-test'
|
||||
FROM device d
|
||||
JOIN household h ON h.tenant_id=d.tenant_id AND h.public_id=$2::uuid
|
||||
JOIN subject_profile sp ON sp.household_id=h.id AND sp.public_id=$3::uuid
|
||||
JOIN user_account ua ON ua.tenant_id=d.tenant_id AND ua.oidc_issuer=$4 AND ua.oidc_subject=$5
|
||||
WHERE d.id=$1::uuid
|
||||
RETURNING public_id::text`, deviceID, "10000000-0000-0000-0000-000000000211", "10000000-0000-0000-0000-000000000411", testIssuer, testOwnerSubject).Scan(&sourceBindingID); err != nil {
|
||||
t.Fatalf("insert source binding: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM audit_event WHERE request_id LIKE 'device-binding-integration-%'`)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM device_binding WHERE device_id=$1::uuid`, deviceID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM device_twin WHERE device_id=$1::uuid`, deviceID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM device WHERE id=$1::uuid`, deviceID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM subject_profile WHERE id=$1::uuid`, targetSubjectID)
|
||||
}()
|
||||
|
||||
service := NewService(pool, audit.NewRepository(), nil, nil)
|
||||
owner := identity.Principal{Issuer: testIssuer, Subject: testOwnerSubject}
|
||||
impact, err := service.GetDeviceBindingImpact(ctx, owner, devicePublicID, DeviceBindingActionTransfer, targetSubjectPublicID)
|
||||
if err != nil || !impact.Allowed || impact.BindingID != sourceBindingID || impact.TargetSubjectID == nil || *impact.TargetSubjectID != targetSubjectPublicID {
|
||||
t.Fatalf("unexpected transfer impact: %+v err=%v", impact, err)
|
||||
}
|
||||
if _, err := service.ChangeDeviceBinding(ctx, owner, devicePublicID, "device-binding-integration-stale", ChangeDeviceBindingInput{
|
||||
Action: DeviceBindingActionTransfer, ExpectedBindingID: uuid.NewString(), TargetSubjectID: targetSubjectPublicID, Reason: "stale review", Acknowledged: true,
|
||||
}); !errors.Is(err, ErrDeviceBindingStale) {
|
||||
t.Fatalf("stale binding error=%v", err)
|
||||
}
|
||||
transferred, err := service.ChangeDeviceBinding(ctx, owner, devicePublicID, "device-binding-integration-transfer", ChangeDeviceBindingInput{
|
||||
Action: DeviceBindingActionTransfer, ExpectedBindingID: impact.BindingID, TargetSubjectID: targetSubjectPublicID, Reason: "change assigned elder", Acknowledged: true,
|
||||
})
|
||||
if err != nil || transferred.BindingID == nil || transferred.SubjectID == nil || *transferred.SubjectID != targetSubjectPublicID {
|
||||
t.Fatalf("transfer result=%+v err=%v", transferred, err)
|
||||
}
|
||||
device, err := service.GetDevice(ctx, owner, devicePublicID)
|
||||
if err != nil || device.BindingID != *transferred.BindingID || device.SubjectID != targetSubjectPublicID {
|
||||
t.Fatalf("transferred device=%+v err=%v", device, err)
|
||||
}
|
||||
unbindImpact, err := service.GetDeviceBindingImpact(ctx, owner, devicePublicID, DeviceBindingActionUnbind, "")
|
||||
if err != nil || !unbindImpact.Allowed {
|
||||
t.Fatalf("unbind impact=%+v err=%v", unbindImpact, err)
|
||||
}
|
||||
unbound, err := service.ChangeDeviceBinding(ctx, owner, devicePublicID, "device-binding-integration-unbind", ChangeDeviceBindingInput{
|
||||
Action: DeviceBindingActionUnbind, ExpectedBindingID: unbindImpact.BindingID, Reason: "device removed from service", Acknowledged: true,
|
||||
})
|
||||
if err != nil || unbound.BindingID != nil {
|
||||
t.Fatalf("unbind result=%+v err=%v", unbound, err)
|
||||
}
|
||||
if _, err := service.GetDevice(ctx, owner, devicePublicID); !errors.Is(err, ErrAccessDenied) {
|
||||
t.Fatalf("unbound device should no longer be visible, error=%v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user