|
- package utils
-
- import (
- "testing"
-
- "github.com/AFASystems/presence/internal/pkg/model"
- )
-
- func TestParseADFast(t *testing.T) {
- tests := []struct {
- name string
- input []byte
- expected [][2]int
- }{
- {
- name: "Empty input",
- input: []byte{},
- expected: [][2]int{},
- },
- {
- name: "Single AD structure",
- input: []byte{0x02, 0x01, 0x06},
- expected: [][2]int{{0, 2}},
- },
- {
- name: "Multiple AD structures",
- input: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
- expected: [][2]int{{0, 2}, {3, 6}},
- },
- {
- name: "Complex AD structures",
- input: []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5},
- expected: [][2]int{{0, 2}, {2, 28}},
- },
- {
- name: "Zero length AD structure",
- input: []byte{0x00, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
- expected: [][2]int{{2, 5}},
- },
- {
- name: "AD structure exceeding bounds",
- input: []byte{0x05, 0x01, 0x06},
- expected: [][2]int{},
- },
- {
- name: "Incomplete AD structure",
- input: []byte{0x03, 0x01},
- expected: [][2]int{},
- },
- {
- name: "Valid then invalid structure",
- input: []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
- expected: [][2]int{{0, 2}},
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := ParseADFast(tt.input)
- if len(result) != len(tt.expected) {
- t.Errorf("ParseADFast() length = %v, expected %v", len(result), len(tt.expected))
- return
- }
-
- for i, r := range result {
- if r[0] != tt.expected[i][0] || r[1] != tt.expected[i][1] {
- t.Errorf("ParseADFast()[%d] = %v, expected %v", i, r, tt.expected[i])
- }
- }
- })
- }
- }
-
- func TestRemoveFlagBytes(t *testing.T) {
- tests := []struct {
- name string
- input []byte
- expected []byte
- }{
- {
- name: "Empty input",
- input: []byte{},
- expected: []byte{},
- },
- {
- name: "Single byte input",
- input: []byte{0x01},
- expected: []byte{0x01},
- },
- {
- name: "No flag bytes",
- input: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
- expected: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01},
- },
- {
- name: "With flag bytes",
- input: []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02},
- expected: []byte{0x1A, 0xFF, 0x4C, 0x00, 0x02},
- },
- {
- name: "Flag type is 0x01",
- input: []byte{0x02, 0x01, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
- expected: []byte{0x05, 0x01, 0x02, 0x03, 0x04},
- },
- {
- name: "Flag type is not 0x01",
- input: []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
- expected: []byte{0x02, 0x02, 0x06, 0x05, 0x01, 0x02, 0x03, 0x04},
- },
- {
- name: "Length exceeds bounds",
- input: []byte{0xFF, 0x01, 0x06},
- expected: []byte{0xFF, 0x01, 0x06},
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := RemoveFlagBytes(tt.input)
- if len(result) != len(tt.expected) {
- t.Errorf("RemoveFlagBytes() length = %v, expected %v", len(result), len(tt.expected))
- return
- }
-
- for i, b := range result {
- if b != tt.expected[i] {
- t.Errorf("RemoveFlagBytes()[%d] = %v, expected %v", i, b, tt.expected[i])
- }
- }
- })
- }
- }
-
- func TestIsValidADStructure(t *testing.T) {
- tests := []struct {
- name string
- data []byte
- expected bool
- }{
- {
- name: "Empty data",
- data: []byte{},
- expected: false,
- },
- {
- name: "Single byte",
- data: []byte{0x01},
- expected: false,
- },
- {
- name: "Valid minimal structure",
- data: []byte{0x01, 0x01},
- expected: true,
- },
- {
- name: "Valid structure",
- data: []byte{0x02, 0x01, 0x06},
- expected: true,
- },
- {
- name: "Zero length",
- data: []byte{0x00, 0x01, 0x06},
- expected: false,
- },
- {
- name: "Length exceeds data",
- data: []byte{0x05, 0x01, 0x06},
- expected: false,
- },
- {
- name: "Length exactly matches",
- data: []byte{0x02, 0x01, 0x06},
- expected: true,
- },
- {
- name: "Large valid structure",
- data: []byte{0x1F, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x01, 0x02, 0x03, 0x04, 0x05},
- expected: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := isValidADStructure(tt.data)
- if result != tt.expected {
- t.Errorf("isValidADStructure() = %v, expected %v", result, tt.expected)
- }
- })
- }
- }
-
- func TestCheckIngics(t *testing.T) {
- tests := []struct {
- name string
- ad []byte
- expected bool
- }{
- {
- name: "Valid Ingics beacon",
- ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
- expected: true,
- },
- {
- name: "Invalid - too short",
- ad: []byte{0x05, 0xFF, 0x59, 0x00},
- expected: false,
- },
- {
- name: "Invalid - wrong manufacturer ID",
- ad: []byte{0x08, 0xFF, 0x59, 0x01, 0x80, 0xBC, 0x12, 0x34, 0x01},
- expected: false,
- },
- {
- name: "Invalid - wrong type",
- ad: []byte{0x08, 0xFE, 0x59, 0x00, 0x80, 0xBC, 0x12, 0x34, 0x01},
- expected: false,
- },
- {
- name: "Valid with minimum length",
- ad: []byte{0x06, 0xFF, 0x59, 0x00, 0x80, 0xBC},
- expected: true,
- },
- {
- name: "Empty data",
- ad: []byte{},
- expected: false,
- },
- {
- name: "Partial match only",
- ad: []byte{0x06, 0xFF, 0x59, 0x00, 0x80},
- expected: false,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := checkIngics(tt.ad)
- if result != tt.expected {
- t.Errorf("checkIngics() = %v, expected %v", result, tt.expected)
- }
- })
- }
- }
-
- func TestParseIngicsState(t *testing.T) {
- tests := []struct {
- name string
- ad []byte
- expected model.BeaconEvent
- }{
- {
- name: "Valid Ingics data",
- ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05},
- expected: model.BeaconEvent{
- Battery: 0x1234, // 4660 in little endian
- Event: 0x05,
- Type: "Ingics",
- },
- },
- {
- name: "Zero battery",
- ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x00, 0x00, 0x00},
- expected: model.BeaconEvent{
- Battery: 0,
- Event: 0,
- Type: "Ingics",
- },
- },
- {
- name: "Max battery value",
- ad: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0xFF, 0xFF, 0xFF},
- expected: model.BeaconEvent{
- Battery: 0xFFFF,
- Event: 0xFF,
- Type: "Ingics",
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := parseIngicsState(tt.ad)
- if result.Battery != tt.expected.Battery {
- t.Errorf("parseIngicsState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
- }
- if result.Event != tt.expected.Event {
- t.Errorf("parseIngicsState() Event = %v, expected %v", result.Event, tt.expected.Event)
- }
- if result.Type != tt.expected.Type {
- t.Errorf("parseIngicsState() Type = %v, expected %v", result.Type, tt.expected.Type)
- }
- })
- }
- }
-
- func TestCheckEddystoneTLM(t *testing.T) {
- tests := []struct {
- name string
- ad []byte
- expected bool
- }{
- {
- name: "Valid Eddystone TLM",
- ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
- expected: true,
- },
- {
- name: "Invalid - too short",
- ad: []byte{0x03, 0x16, 0xAA},
- expected: false,
- },
- {
- name: "Invalid - wrong type",
- ad: []byte{0x12, 0x15, 0xAA, 0xFE, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
- expected: false,
- },
- {
- name: "Invalid - wrong company ID",
- ad: []byte{0x12, 0x16, 0xAA, 0xFF, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04},
- expected: false,
- },
- {
- name: "Invalid - wrong TLM type",
- ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x21, 0x00, 0x01, 0x02, 0x03, 0x04},
- expected: false,
- },
- {
- name: "Valid with minimum length",
- ad: []byte{0x04, 0x16, 0xAA, 0xFE, 0x20},
- expected: true,
- },
- {
- name: "Empty data",
- ad: []byte{},
- expected: false,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := checkEddystoneTLM(tt.ad)
- if result != tt.expected {
- t.Errorf("checkEddystoneTLM() = %v, expected %v", result, tt.expected)
- }
- })
- }
- }
-
- func TestParseEddystoneState(t *testing.T) {
- tests := []struct {
- name string
- ad []byte
- expected model.BeaconEvent
- }{
- {
- name: "Valid Eddystone TLM data",
- ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56, 0x00},
- expected: model.BeaconEvent{
- Battery: 0x1234, // 4660 in big endian (note: different from Ingics)
- Type: "Eddystone",
- },
- },
- {
- name: "Zero battery",
- ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0x00, 0x00, 0x78, 0x56, 0x00},
- expected: model.BeaconEvent{
- Battery: 0,
- Type: "Eddystone",
- },
- },
- {
- name: "Max battery value",
- ad: []byte{0x12, 0x16, 0xAA, 0xFE, 0x20, 0xFF, 0xFF, 0x78, 0x56, 0x00},
- expected: model.BeaconEvent{
- Battery: 0xFFFF,
- Type: "Eddystone",
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := parseEddystoneState(tt.ad)
- if result.Battery != tt.expected.Battery {
- t.Errorf("parseEddystoneState() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
- }
- if result.Type != tt.expected.Type {
- t.Errorf("parseEddystoneState() Type = %v, expected %v", result.Type, tt.expected.Type)
- }
- })
- }
- }
-
- func TestCheckMinewB7(t *testing.T) {
- tests := []struct {
- name string
- ad []byte
- expected bool
- }{
- {
- name: "Valid Minew B7",
- ad: []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
- expected: true,
- },
- {
- name: "Invalid - too short",
- ad: []byte{0x03, 0x16, 0xE1},
- expected: false,
- },
- {
- name: "Invalid - wrong type",
- ad: []byte{0x08, 0x15, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04},
- expected: false,
- },
- {
- name: "Invalid - wrong company ID",
- ad: []byte{0x08, 0x16, 0xE1, 0xFE, 0x01, 0x02, 0x03, 0x04},
- expected: false,
- },
- {
- name: "Valid with minimum length",
- ad: []byte{0x04, 0x16, 0xE1, 0xFF},
- expected: true,
- },
- {
- name: "Empty data",
- ad: []byte{},
- expected: false,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := checkMinewB7(tt.ad)
- if result != tt.expected {
- t.Errorf("checkMinewB7() = %v, expected %v", result, tt.expected)
- }
- })
- }
- }
-
- func TestLoopADStructures(t *testing.T) {
- tests := []struct {
- name string
- data []byte
- ranges [][2]int
- id string
- expected model.BeaconEvent
- }{
- {
- name: "Ingics beacon found",
- data: []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x02, 0x01, 0x06},
- ranges: [][2]int{{0, 8}, {8, 11}},
- id: "test-beacon",
- expected: model.BeaconEvent{
- ID: "test-beacon",
- Name: "test-beacon",
- Battery: 0x1234,
- Event: 0x05,
- Type: "Ingics",
- },
- },
- {
- name: "Eddystone beacon found",
- data: []byte{0x02, 0x01, 0x06, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x34, 0x12, 0x78, 0x56},
- ranges: [][2]int{{0, 2}, {2, 14}},
- id: "eddystone-test",
- expected: model.BeaconEvent{
- ID: "eddystone-test",
- Name: "eddystone-test",
- Battery: 0x1234,
- Type: "Eddystone",
- },
- },
- {
- name: "Minew B7 beacon found",
- data: []byte{0x08, 0x16, 0xE1, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x02, 0x01, 0x06},
- ranges: [][2]int{{0, 8}, {8, 11}},
- id: "minew-test",
- expected: model.BeaconEvent{
- ID: "minew-test",
- Name: "minew-test",
- Type: "", // Minew B7 returns empty BeaconEvent
- },
- },
- {
- name: "No matching beacon type",
- data: []byte{0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02},
- ranges: [][2]int{{0, 2}, {2, 5}},
- id: "unknown-test",
- expected: model.BeaconEvent{},
- },
- {
- name: "Invalid AD structure",
- data: []byte{0x02, 0x01, 0x06, 0xFF, 0x01, 0x06},
- ranges: [][2]int{{0, 2}, {2, 4}},
- id: "invalid-test",
- expected: model.BeaconEvent{},
- },
- {
- name: "Empty data",
- data: []byte{},
- ranges: [][2]int{},
- id: "empty-test",
- expected: model.BeaconEvent{
- ID: "empty-test",
- Name: "empty-test",
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := LoopADStructures(tt.data, tt.ranges, tt.id)
-
- if result.ID != tt.expected.ID {
- t.Errorf("LoopADStructures() ID = %v, expected %v", result.ID, tt.expected.ID)
- }
- if result.Name != tt.expected.Name {
- t.Errorf("LoopADStructures() Name = %v, expected %v", result.Name, tt.expected.Name)
- }
- if result.Type != tt.expected.Type {
- t.Errorf("LoopADStructures() Type = %v, expected %v", result.Type, tt.expected.Type)
- }
- if result.Battery != tt.expected.Battery {
- t.Errorf("LoopADStructures() Battery = %v, expected %v", result.Battery, tt.expected.Battery)
- }
- })
- }
- }
-
- func TestLoopADStructuresPriority(t *testing.T) {
- // Test that Ingics is checked first
- data := []byte{0x08, 0xFF, 0x59, 0x00, 0x80, 0xBC, 0x34, 0x12, 0x05, 0x12, 0x16, 0xAA, 0xFE, 0x20, 0x78, 0x56}
- ranges := [][2]int{{0, 8}, {8, 15}}
-
- result := LoopADStructures(data, ranges, "priority-test")
-
- // Should detect Ingics first, not Eddystone
- if result.Type != "Ingics" {
- t.Errorf("LoopADStructures() Type = %v, expected Ingics (priority test)", result.Type)
- }
- }
-
- // Benchmark tests
- func BenchmarkParseADFast(b *testing.B) {
- data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC5}
-
- for i := 0; i < b.N; i++ {
- ParseADFast(data)
- }
- }
-
- func BenchmarkRemoveFlagBytes(b *testing.B) {
- data := []byte{0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xE2, 0xC5}
-
- for i := 0; i < b.N; i++ {
- RemoveFlagBytes(data)
- }
- }
|