// // DailyNotificationDataConversionsTests.swift // DailyNotificationPluginTests // // Created by Matthew Raymer on 2025-12-08 // Copyright © 2025 TimeSafari. All rights reserved. // import XCTest @testable import DailyNotificationPlugin /** * Unit tests for DailyNotificationDataConversions * * Tests all data type conversion helpers: * - Time conversions (Date ↔ Int64) * - Numeric conversions (Int ↔ Int32, Int64 ↔ Int32) * - String conversions (optional handling, JSON) */ class DailyNotificationDataConversionsTests: XCTestCase { // MARK: - Time Conversion Tests func testDateFromEpochMillis() { // Given: Epoch milliseconds let epochMillis: Int64 = 1609459200000 // 2021-01-01 00:00:00 UTC // When: Convert to Date let date = DailyNotificationDataConversions.dateFromEpochMillis(epochMillis) // Then: Should match expected date let expectedDate = Date(timeIntervalSince1970: 1609459200.0) XCTAssertEqual(date.timeIntervalSince1970, expectedDate.timeIntervalSince1970, accuracy: 0.001, "Date conversion should be accurate") } func testEpochMillisFromDate() { // Given: Date let date = Date(timeIntervalSince1970: 1609459200.0) // 2021-01-01 00:00:00 UTC // When: Convert to epoch milliseconds let epochMillis = DailyNotificationDataConversions.epochMillisFromDate(date) // Then: Should match expected milliseconds XCTAssertEqual(epochMillis, 1609459200000, "Epoch milliseconds should match") } func testDateFromEpochMillis_RoundTrip() { // Given: Original epoch milliseconds let originalMillis: Int64 = 1609459200000 // When: Convert to Date and back let date = DailyNotificationDataConversions.dateFromEpochMillis(originalMillis) let convertedMillis = DailyNotificationDataConversions.epochMillisFromDate(date) // Then: Should match original XCTAssertEqual(convertedMillis, originalMillis, "Round trip conversion should preserve value") } func testDateFromEpochMillis_Optional_Nil() { // Given: Nil optional let optionalMillis: Int64? = nil // When: Convert to optional Date let date = DailyNotificationDataConversions.dateFromEpochMillis(optionalMillis) // Then: Should be nil XCTAssertNil(date, "Nil input should produce nil output") } func testDateFromEpochMillis_Optional_Value() { // Given: Optional with value let optionalMillis: Int64? = 1609459200000 // When: Convert to optional Date let date = DailyNotificationDataConversions.dateFromEpochMillis(optionalMillis) // Then: Should have value XCTAssertNotNil(date, "Non-nil input should produce non-nil output") XCTAssertEqual(date!.timeIntervalSince1970, 1609459200.0, accuracy: 0.001) } func testEpochMillisFromDate_Optional_Nil() { // Given: Nil optional Date let optionalDate: Date? = nil // When: Convert to optional milliseconds let millis = DailyNotificationDataConversions.epochMillisFromDate(optionalDate) // Then: Should be nil XCTAssertNil(millis, "Nil input should produce nil output") } func testEpochMillisFromDate_Optional_Value() { // Given: Optional Date with value let optionalDate: Date? = Date(timeIntervalSince1970: 1609459200.0) // When: Convert to optional milliseconds let millis = DailyNotificationDataConversions.epochMillisFromDate(optionalDate) // Then: Should have value XCTAssertNotNil(millis, "Non-nil input should produce non-nil output") XCTAssertEqual(millis, 1609459200000) } // MARK: - Numeric Conversion Tests func testInt32FromInt() { // Given: Int value let intValue = 42 // When: Convert to Int32 let int32Value = DailyNotificationDataConversions.int32FromInt(intValue) // Then: Should match XCTAssertEqual(int32Value, 42, "Int to Int32 conversion should preserve value") } func testIntFromInt32() { // Given: Int32 value let int32Value: Int32 = 42 // When: Convert to Int let intValue = DailyNotificationDataConversions.intFromInt32(int32Value) // Then: Should match XCTAssertEqual(intValue, 42, "Int32 to Int conversion should preserve value") } func testInt32FromInt64_WithinRange() { // Given: Int64 value within Int32 range let int64Value: Int64 = 2147483647 // Int32.max // When: Convert to Int32 let int32Value = DailyNotificationDataConversions.int32FromInt64(int64Value) // Then: Should match XCTAssertEqual(int32Value, Int32.max, "Int64 to Int32 conversion should work within range") } func testInt32FromInt64_AboveMax() { // Given: Int64 value above Int32.max let int64Value: Int64 = 2147483648 // Int32.max + 1 // When: Convert to Int32 let int32Value = DailyNotificationDataConversions.int32FromInt64(int64Value) // Then: Should be clamped to Int32.max XCTAssertEqual(int32Value, Int32.max, "Int64 above max should be clamped") } func testInt32FromInt64_BelowMin() { // Given: Int64 value below Int32.min let int64Value: Int64 = -2147483649 // Int32.min - 1 // When: Convert to Int32 let int32Value = DailyNotificationDataConversions.int32FromInt64(int64Value) // Then: Should be clamped to Int32.min XCTAssertEqual(int32Value, Int32.min, "Int64 below min should be clamped") } func testInt64FromInt32() { // Given: Int32 value let int32Value: Int32 = 42 // When: Convert to Int64 let int64Value = DailyNotificationDataConversions.int64FromInt32(int32Value) // Then: Should match XCTAssertEqual(int64Value, 42, "Int32 to Int64 conversion should preserve value") } func testInt64FromLong() { // Given: Int64 value (Long) let longValue: Int64 = 42 // When: Convert to Int64 (no-op) let int64Value = DailyNotificationDataConversions.int64FromLong(longValue) // Then: Should match XCTAssertEqual(int64Value, 42, "Long to Int64 conversion should preserve value") } func testBoolFromBoolean() { // Given: Bool value let boolValue = true // When: Convert to Bool (no-op) let convertedBool = DailyNotificationDataConversions.boolFromBoolean(boolValue) // Then: Should match XCTAssertEqual(convertedBool, true, "Boolean to Bool conversion should preserve value") } // MARK: - String Conversion Tests func testStringFromOptional_Nil() { // Given: Nil optional String let optionalString: String? = nil // When: Convert to String let string = DailyNotificationDataConversions.stringFromOptional(optionalString) // Then: Should be empty string XCTAssertEqual(string, "", "Nil optional should produce empty string") } func testStringFromOptional_Value() { // Given: Optional String with value let optionalString: String? = "test" // When: Convert to String let string = DailyNotificationDataConversions.stringFromOptional(optionalString) // Then: Should match XCTAssertEqual(string, "test", "Non-nil optional should produce value") } func testOptionalStringFromString_Empty() { // Given: Empty String let string = "" // When: Convert to optional String let optionalString = DailyNotificationDataConversions.optionalStringFromString(string) // Then: Should be nil XCTAssertNil(optionalString, "Empty string should produce nil") } func testOptionalStringFromString_Value() { // Given: Non-empty String let string = "test" // When: Convert to optional String let optionalString = DailyNotificationDataConversions.optionalStringFromString(string) // Then: Should have value XCTAssertEqual(optionalString, "test", "Non-empty string should produce value") } // MARK: - JSON Conversion Tests func testJsonStringFromDictionary_Valid() { // Given: Valid dictionary let dict: [String: Any] = ["key1": "value1", "key2": 42] // When: Convert to JSON string let jsonString = DailyNotificationDataConversions.jsonStringFromDictionary(dict) // Then: Should be valid JSON XCTAssertNotNil(jsonString, "Valid dictionary should produce JSON string") // Verify can be parsed back let parsedDict = DailyNotificationDataConversions.dictionaryFromJsonString(jsonString) XCTAssertNotNil(parsedDict, "JSON string should be parseable") XCTAssertEqual(parsedDict?["key1"] as? String, "value1") XCTAssertEqual(parsedDict?["key2"] as? Int, 42) } func testJsonStringFromDictionary_Nil() { // Given: Nil dictionary let dict: [String: Any]? = nil // When: Convert to JSON string let jsonString = DailyNotificationDataConversions.jsonStringFromDictionary(dict) // Then: Should be nil XCTAssertNil(jsonString, "Nil dictionary should produce nil") } func testDictionaryFromJsonString_Valid() { // Given: Valid JSON string let jsonString = "{\"key1\":\"value1\",\"key2\":42}" // When: Convert to dictionary let dict = DailyNotificationDataConversions.dictionaryFromJsonString(jsonString) // Then: Should be valid dictionary XCTAssertNotNil(dict, "Valid JSON string should produce dictionary") XCTAssertEqual(dict?["key1"] as? String, "value1") XCTAssertEqual(dict?["key2"] as? Int, 42) } func testDictionaryFromJsonString_Invalid() { // Given: Invalid JSON string let jsonString = "{invalid json}" // When: Convert to dictionary let dict = DailyNotificationDataConversions.dictionaryFromJsonString(jsonString) // Then: Should be nil XCTAssertNil(dict, "Invalid JSON string should produce nil") } func testDictionaryFromJsonString_Nil() { // Given: Nil JSON string let jsonString: String? = nil // When: Convert to dictionary let dict = DailyNotificationDataConversions.dictionaryFromJsonString(jsonString) // Then: Should be nil XCTAssertNil(dict, "Nil JSON string should produce nil") } func testJsonStringFromDictionary_RoundTrip() { // Given: Original dictionary let originalDict: [String: Any] = [ "string": "value", "number": 42, "bool": true, "nested": ["key": "value"] ] // When: Convert to JSON and back let jsonString = DailyNotificationDataConversions.jsonStringFromDictionary(originalDict) let parsedDict = DailyNotificationDataConversions.dictionaryFromJsonString(jsonString) // Then: Should match original (with type conversions) XCTAssertNotNil(parsedDict, "Round trip should produce dictionary") XCTAssertEqual(parsedDict?["string"] as? String, "value") XCTAssertEqual(parsedDict?["number"] as? Int, 42) XCTAssertEqual(parsedDict?["bool"] as? Bool, true) } }