2026-08-29 21:56:51 +08:00
package opsdevice
import (
"context"
"encoding/hex"
"errors"
"os"
2026-10-08 10:06:44 +08:00
"regexp"
"strings"
2026-08-29 21:56:51 +08:00
"testing"
"data-terminal-cloud/internal/audit"
"data-terminal-cloud/internal/deviceauth"
2026-10-08 10:06:44 +08:00
"data-terminal-cloud/internal/devicebinding"
2026-08-29 21:56:51 +08:00
"data-terminal-cloud/internal/identity"
"github.com/jackc/pgx/v5/pgxpool"
)
2026-10-08 10:06:44 +08:00
func TestOperationsDeviceCreatesPhysicalConfirmIdentity ( t * testing . T ) {
2026-08-29 21:56:51 +08:00
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 ()
resolver , err := deviceauth . NewDatabaseSecretResolver ( pool , "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" )
if err != nil {
t . Fatal ( err )
}
2026-10-08 10:06:44 +08:00
service , err := NewService ( pool , resolver , audit . NewRepository (), Options {
OnboardingMasterKey : strings . Repeat ( "15" , 32 ),
DeviceOrigin : "https://platform.example/onboarding/entry" ,
2026-08-29 21:56:51 +08:00
})
if err != nil {
t . Fatal ( err )
}
principal := integrationOperatorPrincipal ( t , ctx , pool )
const tenantID = "10000000-0000-0000-0000-000000000010"
2026-10-08 10:06:44 +08:00
created , err := service . Create ( ctx , principal , tenantID , "ops-device-integration-create" , CreateInput { Model : "ECHOEAR-S3" })
2026-08-29 21:56:51 +08:00
if err != nil {
t . Fatal ( err )
}
deviceID := created . Device . ID
defer func () {
_ , _ = pool . Exec ( ctx , `DELETE FROM audit_event WHERE resource_id=$1::uuid` , deviceID )
2026-09-06 17:04:11 +08:00
_ , _ = pool . Exec ( ctx , `DELETE FROM device_activation_transition WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)` , deviceID )
2026-10-08 10:06:44 +08:00
_ , _ = pool . Exec ( ctx , `DELETE FROM device_onboarding_key WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)` , deviceID )
2026-08-29 21:56:51 +08:00
_ , _ = pool . Exec ( ctx , `DELETE FROM device_twin WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)` , deviceID )
_ , _ = pool . Exec ( ctx , `DELETE FROM device WHERE public_id=$1::uuid` , deviceID )
}()
2026-10-08 10:06:44 +08:00
credential := created . Credential
if ! regexp . MustCompile ( `^KP01-[0-9A-F]{12}$` ). MatchString ( credential . FactoryID ) || credential . FactoryID != created . Device . FactoryID || credential . Model != "ECHOEAR-S3" || credential . Origin != "https://platform.example" || credential . KeyVersion != 1 {
t . Fatalf ( "unexpected burn-in identity: %+v" , credential )
2026-08-29 21:56:51 +08:00
}
2026-10-08 10:06:44 +08:00
key , err := hex . DecodeString ( credential . DeviceKeyHex )
if err != nil || len ( key ) != 32 {
t . Fatalf ( "invalid device key: len=%d err=%v" , len ( key ), err )
2026-08-29 21:56:51 +08:00
}
2026-10-08 10:06:44 +08:00
if created . Device . EnrollmentMode != "physical_confirm" || created . Device . ActivationState != "CLAIMABLE" || created . Device . Bound || created . Device . CredentialStatus != "active" || created . Device . KeyVersion != 1 {
t . Fatalf ( "unexpected created device: %+v" , created . Device )
2026-08-29 21:56:51 +08:00
}
2026-10-08 10:06:44 +08:00
var released bool
var desiredConfig string
var activeKeys , legacyCredentials , claims , bindings int
if err := pool . QueryRow ( ctx , `SELECT onboarding_released_at IS NOT NULL FROM device WHERE public_id=$1::uuid` , deviceID ). Scan ( & released ); err != nil {
2026-08-29 21:56:51 +08:00
t . Fatal ( err )
}
2026-10-08 10:06:44 +08:00
if err := pool . QueryRow ( ctx , `SELECT desired_config::text FROM device_twin WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)` , deviceID ). Scan ( & desiredConfig ); err != nil {
t . Fatal ( err )
}
if err := pool . QueryRow ( ctx , `SELECT
(SELECT count(*) FROM device_onboarding_key WHERE device_id=d.id AND status='ACTIVE'),
(SELECT count(*) FROM device_credential WHERE device_id=d.id),
(SELECT count(*) FROM device_claim WHERE device_id=d.id),
(SELECT count(*) FROM device_binding WHERE device_id=d.id AND unbound_at IS NULL)
FROM device d WHERE d.public_id=$1::uuid` , deviceID ). Scan ( & activeKeys , & legacyCredentials , & claims , & bindings ); err != nil {
t . Fatal ( err )
}
if ! released || desiredConfig != "{}" || activeKeys != 1 || legacyCredentials != 0 || claims != 0 || bindings != 0 {
t . Fatalf ( "device was not created as direct BLE inventory: released=%v desired=%s active_keys=%d legacy_credentials=%d claims=%d bindings=%d" , released , desiredConfig , activeKeys , legacyCredentials , claims , bindings )
}
if _ , err := service . IssueDeviceClaim ( ctx , principal , tenantID , deviceID , "ops-device-integration-legacy-claim" ); ! errors . Is ( err , devicebinding . ErrConfirmationRequired ) {
t . Fatalf ( "physical-confirm device accepted a legacy claim: %v" , err )
}
rotated , err := service . RotateCredential ( ctx , principal , tenantID , deviceID , "ops-device-integration-rotate" )
if err != nil || rotated . KeyVersion != 2 || rotated . DeviceKeyHex == credential . DeviceKeyHex || rotated . FactoryID != credential . FactoryID {
t . Fatalf ( "unexpected credential rotation: %+v err=%v" , rotated , err )
}
var activeVersion , revokedKeys int
if err := pool . QueryRow ( ctx , `SELECT max(key_version) FILTER (WHERE status='ACTIVE'),count(*) FILTER (WHERE status='REVOKED') FROM device_onboarding_key WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)` , deviceID ). Scan ( & activeVersion , & revokedKeys ); err != nil {
t . Fatal ( err )
}
if activeVersion != 2 || revokedKeys != 1 {
t . Fatalf ( "unexpected rotated key state: active=%d revoked=%d" , activeVersion , revokedKeys )
}
var version int64
if err := pool . QueryRow ( ctx , `SELECT version FROM device WHERE public_id=$1::uuid` , deviceID ). Scan ( & version ); err != nil {
t . Fatal ( err )
}
retired , err := service . Delete ( ctx , principal , tenantID , deviceID , "ops-device-integration-delete" , version )
if err != nil || retired . Status != "revoked" || retired . ActivationState != "REVOKED" || retired . CredentialStatus != "missing" {
t . Fatalf ( "unexpected delete result: %+v err=%v" , retired , err )
2026-08-29 21:56:51 +08:00
}
}
func integrationOperatorPrincipal ( t * testing . T , ctx context . Context , pool * pgxpool . Pool ) identity . Principal {
t . Helper ()
var principal identity . Principal
if err := pool . QueryRow ( ctx , `SELECT oidc_issuer,oidc_subject FROM user_account WHERE public_id='10000000-0000-0000-0000-000000000114'::uuid AND status='active' AND account_kind='operator'` ). Scan ( & principal . Issuer , & principal . Subject ); err != nil {
t . Fatalf ( "resolve integration operator: %v" , err )
}
return principal
}