Refactor code for improved readability and maintainability; add utility functions for score calculation and archive processing

This commit is contained in:
ProtoTess
2026-01-16 05:55:37 +00:00
parent c701115620
commit 268330840c
10 changed files with 208 additions and 146 deletions
+15 -21
View File
@@ -273,6 +273,14 @@ func BuildMatchKeyFromExchange(exchange *CapturedExchangeV2) MatchKey {
}
}
// addTokenScore adds 10 points to score if token matches between two MatchKeys.
func addTokenScore(score int, token1, token2 string) int {
if token1 != "" && token1 == token2 {
return score + 10
}
return score
}
// MatchScore returns how well two MatchKeys match (higher is better).
// Returns -1 if operation names don't match.
func (k MatchKey) MatchScore(other MatchKey) int {
@@ -283,27 +291,13 @@ func (k MatchKey) MatchScore(other MatchKey) int {
score := 1 // Base score for matching operation
// Bonus points for matching parameters
if k.ProfileToken != "" && k.ProfileToken == other.ProfileToken {
score += 10
}
if k.ConfigurationToken != "" && k.ConfigurationToken == other.ConfigurationToken {
score += 10
}
if k.VideoSourceToken != "" && k.VideoSourceToken == other.VideoSourceToken {
score += 10
}
if k.AudioSourceToken != "" && k.AudioSourceToken == other.AudioSourceToken {
score += 10
}
if k.PresetToken != "" && k.PresetToken == other.PresetToken {
score += 10
}
if k.NodeToken != "" && k.NodeToken == other.NodeToken {
score += 10
}
if k.OSDToken != "" && k.OSDToken == other.OSDToken {
score += 10
}
score = addTokenScore(score, k.ProfileToken, other.ProfileToken)
score = addTokenScore(score, k.ConfigurationToken, other.ConfigurationToken)
score = addTokenScore(score, k.VideoSourceToken, other.VideoSourceToken)
score = addTokenScore(score, k.AudioSourceToken, other.AudioSourceToken)
score = addTokenScore(score, k.PresetToken, other.PresetToken)
score = addTokenScore(score, k.NodeToken, other.NodeToken)
score = addTokenScore(score, k.OSDToken, other.OSDToken)
return score
}
+8 -6
View File
@@ -2,6 +2,7 @@
package onviftesting
import (
"bytes"
"encoding/json"
"fmt"
"os"
@@ -40,7 +41,7 @@ type GoldenFileSet struct {
// LoadGoldenManifest loads a manifest.json from a golden directory.
func LoadGoldenManifest(goldenDir string) (*GoldenManifest, error) {
manifestPath := filepath.Join(goldenDir, "manifest.json")
data, err := os.ReadFile(manifestPath)
data, err := os.ReadFile(manifestPath) //nolint:gosec // Path is from test data directory, safe
if err != nil {
return nil, fmt.Errorf("failed to read manifest: %w", err)
}
@@ -82,7 +83,7 @@ func LoadGoldenFiles(goldenDir string) (*GoldenFileSet, error) {
return nil
}
data, err := os.ReadFile(path)
data, err := os.ReadFile(path) //nolint:gosec // Path is from filepath.Walk, safe
if err != nil {
return fmt.Errorf("failed to read %s: %w", path, err)
}
@@ -100,7 +101,7 @@ func LoadGoldenFiles(goldenDir string) (*GoldenFileSet, error) {
})
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to load golden files: %w", err)
}
return set, nil
@@ -171,6 +172,7 @@ func ValidateResponse(response interface{}, golden *GoldenFile) []string {
actual, ok := responseData[field]
if !ok {
errors = append(errors, fmt.Sprintf("missing field: %s", field))
continue
}
@@ -192,12 +194,12 @@ func ValidateResponse(response interface{}, golden *GoldenFile) []string {
func toMap(v interface{}) (map[string]interface{}, error) {
data, err := json.Marshal(v)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to marshal value: %w", err)
}
var result map[string]interface{}
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
return nil, fmt.Errorf("failed to unmarshal to map: %w", err)
}
return result, nil
@@ -230,7 +232,7 @@ func valuesEqual(expected, actual interface{}) bool {
return false
}
return string(e) == string(a)
return bytes.Equal(e, a)
}
// SaveGoldenFile saves a golden file to disk.
+59 -33
View File
@@ -263,6 +263,58 @@ func NewMockSOAPServerV2(archivePath string) (*MockSOAPServerV2, error) {
return mock, nil
}
// processArchiveEntry processes a single tar archive entry (JSON file) and adds it to the capture.
// Returns (isMetadata, error).
func processArchiveEntry(header *tar.Header, data []byte, capture *CameraCaptureV2) (*CaptureMetadata, error) {
// Check for metadata.json (V2 archives)
if header.Name == "metadata.json" || strings.HasSuffix(header.Name, "/metadata.json") {
var meta CaptureMetadata
if err := json.Unmarshal(data, &meta); err != nil {
return nil, fmt.Errorf("failed to unmarshal metadata: %w", err)
}
return &meta, nil
}
// Skip files that look like request/response XML stored as JSON
if strings.Contains(header.Name, "_request") || strings.Contains(header.Name, "_response") {
return nil, nil
}
// Parse exchange from JSON
exchange, err := parseExchange(header.Name, data)
if err != nil {
return nil, err
}
if exchange != nil {
capture.Exchanges = append(capture.Exchanges, *exchange)
}
return nil, nil
}
// parseExchange parses a JSON exchange entry, supporting both V1 and V2 formats.
func parseExchange(fileName string, data []byte) (*CapturedExchangeV2, error) {
version := DetectCaptureVersion(data)
if version >= "2.0" {
var exchange CapturedExchangeV2
if err := json.Unmarshal(data, &exchange); err != nil {
return nil, fmt.Errorf("failed to unmarshal V2 %s: %w", fileName, err)
}
return &exchange, nil
}
// V1 format - convert to V2
var v1Exchange CapturedExchange
if err := json.Unmarshal(data, &v1Exchange); err != nil {
return nil, fmt.Errorf("failed to unmarshal V1 %s: %w", fileName, err)
}
v2Exchange := ConvertV1ToV2(&v1Exchange)
// Extract parameters from V1 request body
v2Exchange.Parameters = ExtractParameters(v2Exchange.OperationName, v2Exchange.RequestBody)
v2Exchange.ServiceType = DetermineServiceType(v2Exchange.RequestBody)
return v2Exchange, nil
}
// LoadCaptureFromArchiveV2 loads captures from archive, supporting both V1 and V2 formats.
func LoadCaptureFromArchiveV2(archivePath string) (*CameraCaptureV2, *CaptureMetadata, error) {
file, err := os.Open(archivePath) //nolint:gosec // File path is from test data, safe
@@ -308,40 +360,13 @@ func LoadCaptureFromArchiveV2(archivePath string) (*CameraCaptureV2, *CaptureMet
return nil, nil, fmt.Errorf("failed to read file %s: %w", header.Name, err)
}
// Check for metadata.json (V2 archives)
if header.Name == "metadata.json" || strings.HasSuffix(header.Name, "/metadata.json") {
var meta CaptureMetadata
if err := json.Unmarshal(data, &meta); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal metadata: %w", err)
}
metadata = &meta
continue
// Process the archive entry
meta, err := processArchiveEntry(header, data, capture)
if err != nil {
return nil, nil, err
}
// Skip files that look like request/response XML stored as JSON
if strings.Contains(header.Name, "_request") || strings.Contains(header.Name, "_response") {
continue
}
// Detect version and unmarshal accordingly
version := DetectCaptureVersion(data)
if version >= "2.0" {
var exchange CapturedExchangeV2
if err := json.Unmarshal(data, &exchange); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal V2 %s: %w", header.Name, err)
}
capture.Exchanges = append(capture.Exchanges, exchange)
} else {
// V1 format - convert to V2
var v1Exchange CapturedExchange
if err := json.Unmarshal(data, &v1Exchange); err != nil {
return nil, nil, fmt.Errorf("failed to unmarshal V1 %s: %w", header.Name, err)
}
v2Exchange := ConvertV1ToV2(&v1Exchange)
// Extract parameters from V1 request body
v2Exchange.Parameters = ExtractParameters(v2Exchange.OperationName, v2Exchange.RequestBody)
v2Exchange.ServiceType = DetermineServiceType(v2Exchange.RequestBody)
capture.Exchanges = append(capture.Exchanges, *v2Exchange)
if meta != nil {
metadata = meta
}
}
@@ -496,6 +521,7 @@ func ExtractParameters(operationName, soapBody string) map[string]interface{} {
for i := 1; i < len(matches); i++ {
if matches[i] != "" {
params[paramName] = strings.TrimSpace(matches[i])
break
}
}
+2 -1
View File
@@ -417,9 +417,10 @@ func ReadOperationsByService(service ServiceType) []OperationSpec {
return EventReadOperations
case ServiceDeviceIO:
return DeviceIOReadOperations
default:
case ServiceUnknown:
return nil
}
return nil
}
// IndependentOperations returns operations that don't depend on other operations.
+16 -9
View File
@@ -9,6 +9,8 @@ import (
"time"
)
const percentScale = 100
// Registry holds information about all available camera captures.
type Registry struct {
Version string `json:"version"`
@@ -47,7 +49,7 @@ const DefaultRegistryPath = "testdata/captures/registry.json"
// LoadRegistry loads the capture registry from a file.
func LoadRegistry(path string) (*Registry, error) {
data, err := os.ReadFile(path)
data, err := os.ReadFile(path) //nolint:gosec // Registry path is from constant or test data, safe
if err != nil {
if os.IsNotExist(err) {
// Return empty registry if file doesn't exist
@@ -92,12 +94,13 @@ func SaveRegistry(registry *Registry, path string) error {
}
// AddCamera adds a new camera to the registry.
func (r *Registry) AddCamera(entry CameraEntry) {
func (r *Registry) AddCamera(entry *CameraEntry) {
// Check if camera already exists
for i, cam := range r.Cameras {
for i := range r.Cameras {
cam := &r.Cameras[i]
if cam.ID == entry.ID {
// Update existing entry
r.Cameras[i] = entry
r.Cameras[i] = *entry
return
}
}
@@ -106,7 +109,7 @@ func (r *Registry) AddCamera(entry CameraEntry) {
if entry.AddedDate == "" {
entry.AddedDate = time.Now().Format("2006-01-02")
}
r.Cameras = append(r.Cameras, entry)
r.Cameras = append(r.Cameras, *entry)
}
// GetCamera retrieves a camera entry by ID.
@@ -121,12 +124,14 @@ func (r *Registry) GetCamera(id string) *CameraEntry {
// RemoveCamera removes a camera from the registry.
func (r *Registry) RemoveCamera(id string) bool {
for i, cam := range r.Cameras {
for i := range r.Cameras {
cam := &r.Cameras[i]
if cam.ID == id {
r.Cameras = append(r.Cameras[:i], r.Cameras[i+1:]...)
return true
}
}
return false
}
@@ -164,7 +169,7 @@ func (r *Registry) UpdateCoverage() {
}
// GetTotalCoverage returns the total coverage across all services.
func (r *Registry) GetTotalCoverage() (total int, captured int) {
func (r *Registry) GetTotalCoverage() (total, captured int) {
for _, cov := range r.Coverage {
total += cov.Total
captured += cov.Captured
@@ -238,6 +243,7 @@ func CreateCameraEntryFromCapture(archivePath string) (*CameraEntry, error) {
cameraInfo.Manufacturer = ExtractXMLElement(ex.ResponseBody, "Manufacturer")
cameraInfo.Model = ExtractXMLElement(ex.ResponseBody, "Model")
cameraInfo.FirmwareVersion = ExtractXMLElement(ex.ResponseBody, "FirmwareVersion")
break
}
}
@@ -280,10 +286,11 @@ func detectCapabilities(capture *CameraCaptureV2) []string {
}
}
var result []string
result := make([]string, 0, len(services))
for svc := range services {
result = append(result, svc)
}
return result
}
@@ -358,7 +365,7 @@ func (r *Registry) GetSummary() RegistrySummary {
summary.TotalOperations += cov.Total
summary.CapturedOperations += cov.Captured
if cov.Total > 0 {
summary.ServiceCoverage[service] = float64(cov.Captured) / float64(cov.Total) * 100
summary.ServiceCoverage[service] = float64(cov.Captured) / float64(cov.Total) * percentScale
}
}