OpenAIIndeedWebSearchTests.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. //
  2. // OpenAIIndeedWebSearchTests.swift
  3. // indeed_ai_job_searchTests
  4. //
  5. import XCTest
  6. @testable import indeed_ai_job_search
  7. final class OpenAIResponsesClientParsingTests: XCTestCase {
  8. func testExtractsConvenienceOutputText() throws {
  9. let json = """
  10. { "output_text": "[{ \\"title\\": \\"iOS\\" }]" }
  11. """.data(using: .utf8)!
  12. let text = try OpenAIResponsesClient.extractOutputText(from: json)
  13. XCTAssertTrue(text.contains("iOS"))
  14. }
  15. func testExtractsMessageOutputParts() throws {
  16. let json = """
  17. {
  18. "output": [
  19. { "type": "web_search_call", "status": "completed" },
  20. {
  21. "type": "message",
  22. "role": "assistant",
  23. "content": [
  24. { "type": "output_text", "text": "[{\\"title\\":\\"Swift Engineer\\"}]" }
  25. ]
  26. }
  27. ]
  28. }
  29. """.data(using: .utf8)!
  30. let text = try OpenAIResponsesClient.extractOutputText(from: json)
  31. XCTAssertTrue(text.contains("Swift Engineer"))
  32. }
  33. }
  34. final class OpenAIIndeedWebSearchParsingTests: XCTestCase {
  35. func testDecodesMarkdownFencedJSON() throws {
  36. let output = """
  37. ```json
  38. [
  39. {
  40. "title": "iOS Developer",
  41. "company": "Tuyo",
  42. "location": "Remote",
  43. "url": "https://www.indeed.com/viewjob?jk=5185d0dd8c286093",
  44. "jobKey": "5185d0dd8c286093",
  45. "remoteType": "remote",
  46. "salaryMin": 120000,
  47. "salaryMax": 160000,
  48. "salaryCurrency": "USD",
  49. "salaryPeriod": "year",
  50. "posted_days_ago": 2,
  51. "accepting_applications": true
  52. }
  53. ]
  54. ```
  55. """
  56. let dtos = try OpenAIIndeedWebSearchDataSource.decodeJobDTOs(from: output)
  57. XCTAssertEqual(dtos.count, 1)
  58. let listing = try IndeedListingMapper.map(dtos[0])
  59. XCTAssertEqual(listing.company, "Tuyo")
  60. XCTAssertEqual(listing.indeedJobKey, "5185d0dd8c286093")
  61. XCTAssertTrue(listing.indeedURL.absoluteString.contains("viewjob"))
  62. XCTAssertEqual(listing.salary?.currency, "USD")
  63. XCTAssertFalse(IndeedBrowseURLBuilder.openableURL(for: listing).usedBrowseFallback)
  64. }
  65. func testDropsRowsMissingTitleOrURL() throws {
  66. let output = """
  67. [
  68. { "title": "", "company": "Acme", "url": "https://www.indeed.com/viewjob?jk=aaaaaaaaaaaaaaaa", "posted_days_ago": 1, "accepting_applications": true },
  69. { "title": "iOS", "company": "Acme", "url": "https://www.indeed.com/viewjob?jk=bbbbbbbbbbbbbbbb", "posted_days_ago": 1, "accepting_applications": true }
  70. ]
  71. """
  72. let dtos = try OpenAIIndeedWebSearchDataSource.decodeJobDTOs(from: output)
  73. XCTAssertEqual(dtos.count, 1)
  74. XCTAssertEqual(dtos[0].title, "iOS")
  75. }
  76. func testDropsStaleAndClosedButKeepsUndatedOpen() throws {
  77. let output = """
  78. [
  79. {
  80. "title": "Old iOS Role",
  81. "company": "Stale Co",
  82. "url": "https://www.indeed.com/viewjob?jk=1111111111111111",
  83. "posted_days_ago": 90,
  84. "accepting_applications": true
  85. },
  86. {
  87. "title": "Closed iOS Role",
  88. "company": "Closed Co",
  89. "url": "https://www.indeed.com/viewjob?jk=2222222222222222",
  90. "posted_days_ago": 1,
  91. "accepting_applications": false
  92. },
  93. {
  94. "title": "Fresh iOS Role",
  95. "company": "Fresh Co",
  96. "url": "https://www.indeed.com/viewjob?jk=3333333333333333",
  97. "posted_days_ago": 3,
  98. "accepting_applications": true
  99. },
  100. {
  101. "title": "Undated iOS Role",
  102. "company": "Mystery Co",
  103. "url": "https://www.indeed.com/viewjob?jk=4444444444444444",
  104. "accepting_applications": true
  105. }
  106. ]
  107. """
  108. let rows = try OpenAIIndeedWebSearchDataSource.decodeJobRows(from: output)
  109. XCTAssertEqual(rows.count, 4)
  110. let kept = rows.filter(\.isAcceptingApplications).compactMap {
  111. $0.asIndeedJobDTO(
  112. maxAgeDays: OpenAIIndeedWebSearchDataSource.maxPostedDaysAgo,
  113. allowMissingDate: true
  114. )
  115. }
  116. XCTAssertEqual(kept.map(\.company).sorted(), ["Fresh Co", "Mystery Co"])
  117. }
  118. func testPromptRequiresRecentOpenJobs() {
  119. var criteria = SearchCriteria()
  120. criteria.jobTitle = "iOS Developer"
  121. criteria.country = "Pakistan"
  122. criteria.remoteTypes = [.remote]
  123. let prompt = OpenAIIndeedWebSearchDataSource.buildPrompt(
  124. query: "remote iOS in Pakistan",
  125. criteria: criteria,
  126. maxResults: 10,
  127. maxAgeDays: 14
  128. )
  129. XCTAssertTrue(prompt.lowercased().contains("indeed"))
  130. XCTAssertTrue(prompt.contains("Pakistan"))
  131. XCTAssertTrue(prompt.contains("fromage="))
  132. XCTAssertTrue(prompt.lowercased().contains("no longer accepting") || prompt.lowercased().contains("expired"))
  133. XCTAssertTrue(prompt.contains("posted_days_ago"))
  134. }
  135. func testWebSearchModelConstantIsSet() {
  136. XCTAssertFalse(OpenAIModels.defaultWebSearchModel.isEmpty)
  137. XCTAssertNotEqual(OpenAIModels.defaultWebSearchModel, OpenAIModels.defaultChatModel)
  138. }
  139. }