property_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package icq_legacy
  2. import (
  3. "context"
  4. "log/slog"
  5. "strconv"
  6. "testing"
  7. "testing/quick"
  8. "github.com/mk6i/open-oscar-server/state"
  9. "github.com/mk6i/open-oscar-server/wire"
  10. "github.com/stretchr/testify/mock"
  11. )
  12. // TestProperty_UINScreenNameRoundTrip verifies Property 8: UIN↔ScreenName round trip.
  13. // For any valid UIN (1 ≤ UIN ≤ 999999999), converting to state.IdentScreenName
  14. // via strconv.FormatUint(uint64(uin), 10) and back via
  15. // strconv.ParseUint(screenName.String(), 10, 32) produces the original UIN.
  16. //
  17. // **Validates: Requirements 7.1, 7.2**
  18. func TestProperty_UINScreenNameRoundTrip(t *testing.T) {
  19. f := func(raw uint32) bool {
  20. // Clamp to valid UIN range [1, 999999999]
  21. uin := (raw % 999999999) + 1
  22. // Forward: UIN → IdentScreenName
  23. screenName := state.NewIdentScreenName(strconv.FormatUint(uint64(uin), 10))
  24. // Reverse: IdentScreenName → UIN
  25. parsed, err := strconv.ParseUint(screenName.String(), 10, 32)
  26. if err != nil {
  27. return false
  28. }
  29. return uint32(parsed) == uin
  30. }
  31. if err := quick.Check(f, nil); err != nil {
  32. t.Errorf("UIN↔ScreenName round trip property failed: %v", err)
  33. }
  34. }
  35. // TestProperty_ServiceBehavioralEquivalence verifies Property 9: Service behavioral equivalence.
  36. // For any valid AuthRequest (UIN 1-999999999, non-empty password), the service
  37. // returns a consistent AuthResult. Specifically, for a user that doesn't exist,
  38. // it always returns {Success: false, ErrorCode: 0x0002}.
  39. //
  40. // **Validates: Requirements 8.1, 8.2**
  41. func TestProperty_ServiceBehavioralEquivalence(t *testing.T) {
  42. f := func(raw uint32, passByte byte) bool {
  43. // Clamp to valid UIN range [1, 999999999]
  44. uin := (raw % 999999999) + 1
  45. // Generate a non-empty password from the random byte
  46. password := string([]byte{'a' + passByte%26})
  47. authSvc := newMockAuthService(t)
  48. authSvc.EXPECT().FLAPLogin(mock.Anything, mock.Anything, "").
  49. Return(wire.TLVRestBlock{
  50. TLVList: []wire.TLV{
  51. wire.NewTLVBE(wire.LoginTLVTagsErrorSubcode, wire.LoginErrICQUserErr),
  52. },
  53. }, nil)
  54. svc := NewICQLegacyService(
  55. authSvc,
  56. newMockUserManager(t),
  57. newMockAccountManager(t),
  58. newMockSessionRetriever(t),
  59. newMockMessageRelayer(t),
  60. newMockBuddyBroadcaster(t),
  61. newMockOfflineMessageManager(t),
  62. newMockICQUserFinder(t),
  63. newMockICQUserUpdater(t),
  64. newMockFeedbagManager(t),
  65. newMockRelationshipFetcher(t),
  66. newMockBuddyListRegistry(t),
  67. newMockBuddyService(t),
  68. newMockICBMService(t),
  69. &LegacySessionManager{sessions: map[uint32]*LegacySession{}},
  70. slog.Default(),
  71. )
  72. result, err := svc.AuthenticateUser(context.Background(), AuthRequest{
  73. UIN: uin,
  74. Password: password,
  75. })
  76. if err != nil {
  77. return false
  78. }
  79. // For a non-existent user, the service must always return
  80. // Success=false with ErrorCode=0x0002 (user not found)
  81. return !result.Success && result.ErrorCode == 0x0002
  82. }
  83. if err := quick.Check(f, nil); err != nil {
  84. t.Errorf("Service behavioral equivalence property failed: %v", err)
  85. }
  86. }