diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e01ed80..393c27a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Lint + run: swift format lint --recursive . --strict + - name: Build run: swift build -v diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..2fce0f0 --- /dev/null +++ b/.swift-format @@ -0,0 +1,11 @@ +{ + "version": 1, + "indentation": { + "spaces": 4 + }, + "lineLength": 120, + "maximumBlankLines": 1, + "respectsExistingLineBreaks": true, + "lineBreakBeforeEachArgument": true, + "multiElementCollectionTrailingCommas": true +} \ No newline at end of file diff --git a/Package.swift b/Package.swift index 5ba605c..b7e2404 100644 --- a/Package.swift +++ b/Package.swift @@ -17,13 +17,15 @@ let package = Package( // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "EventSource", - targets: ["EventSource"]) + targets: ["EventSource"] + ) ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. // Targets can depend on other targets in this package and products from dependencies. .target( - name: "EventSource"), + name: "EventSource" + ), .testTarget( name: "EventSourceTests", dependencies: ["EventSource"] diff --git a/Sources/EventSource/EventSource.swift b/Sources/EventSource/EventSource.swift index 8a20a7d..27b3697 100644 --- a/Sources/EventSource/EventSource.swift +++ b/Sources/EventSource/EventSource.swift @@ -396,7 +396,9 @@ public actor EventSource { // Perform the HTTP request and get an asynchronous byte stream. let (byteStream, response) = try await session.bytes( - for: currentRequest, delegate: nil) + for: currentRequest, + delegate: nil + ) // Validate HTTP response (status code and content type). if let httpResponse = response as? HTTPURLResponse { diff --git a/Tests/EventSourceTests/Helpers/MockURLProtocol.swift b/Tests/EventSourceTests/Helpers/MockURLProtocol.swift index 0693afc..4e04f45 100644 --- a/Tests/EventSourceTests/Helpers/MockURLProtocol.swift +++ b/Tests/EventSourceTests/Helpers/MockURLProtocol.swift @@ -86,7 +86,8 @@ final class MockURLProtocol: URLProtocol, @unchecked Sendable { /// A test trait to set up and clean up mock URL protocol handlers struct MockURLSessionTestTrait: TestTrait, TestScoping { func provideScope( - for test: Test, testCase: Test.Case?, + for test: Test, + testCase: Test.Case?, performing function: @Sendable () async throws -> Void ) async throws { // Clear handler before test diff --git a/Tests/EventSourceTests/IntegrationTests.swift b/Tests/EventSourceTests/IntegrationTests.swift index bad3b55..c444f73 100644 --- a/Tests/EventSourceTests/IntegrationTests.swift +++ b/Tests/EventSourceTests/IntegrationTests.swift @@ -110,7 +110,8 @@ import Testing #expect((response as? HTTPURLResponse)?.statusCode == 200) #expect( (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "Content-Type") - == "text/event-stream") + == "text/event-stream" + ) // Stream events asynchronously for try await event in byteStream.events { @@ -167,7 +168,8 @@ import Testing #expect(request.value(forHTTPHeaderField: "Content-Type") == "application/json") #expect( request.value(forHTTPHeaderField: "Authorization")?.starts(with: "Bearer ") - == true) + == true + ) // Verify request body if let bodyData = request.httpBody { @@ -209,7 +211,8 @@ import Testing #expect((response as? HTTPURLResponse)?.statusCode == 200) #expect( (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "Content-Type") - == "text/event-stream") + == "text/event-stream" + ) // Stream events asynchronously for try await event in byteStream.events { @@ -540,7 +543,8 @@ import Testing #expect((response as? HTTPURLResponse)?.statusCode == 200) #expect( (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "Content-Type") - == "text/event-stream") + == "text/event-stream" + ) // Use the events extension to convert bytes to SSE events let eventsSequence = byteStream.events @@ -717,7 +721,9 @@ import Testing // Create EventSource with the custom session let eventSource = EventSource( - request: request, configuration: session.configuration) + request: request, + configuration: session.configuration + ) // Set up error handler let errorTracker = ErrorTracker() @@ -794,7 +800,8 @@ import Testing #expect((response as? HTTPURLResponse)?.statusCode == 200) #expect( (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "Content-Type") - == "text/event-stream") + == "text/event-stream" + ) // Stream events asynchronously for try await event in byteStream.events { @@ -857,7 +864,8 @@ import Testing // Reference to the protocol instance (need to do this via reflection) guard let protocolInstance = request.value( - forHTTPHeaderField: "_MockURLProtocolInstance") as? NSObjectProtocol + forHTTPHeaderField: "_MockURLProtocolInstance" + ) as? NSObjectProtocol else { return } @@ -923,7 +931,8 @@ import Testing } func setClientAndProtocol( - client: NSObjectProtocol, protocolInstance: NSObjectProtocol + client: NSObjectProtocol, + protocolInstance: NSObjectProtocol ) { self.client = client self.protocolInstance = protocolInstance diff --git a/Tests/EventSourceTests/ParserTests.swift b/Tests/EventSourceTests/ParserTests.swift index 8634824..b464f5f 100644 --- a/Tests/EventSourceTests/ParserTests.swift +++ b/Tests/EventSourceTests/ParserTests.swift @@ -83,7 +83,8 @@ struct ParserTests { // Check that lastEventId was not updated with the NUL-containing ID #expect( await parser.getLastEventId() == "", - "LastEventId should not be set to a value containing NUL.") + "LastEventId should not be set to a value containing NUL." + ) // Check the dispatched event var dispatchedEvents: [EventSource.Event] = [] @@ -94,7 +95,8 @@ struct ParserTests { #expect(dispatchedEvents.count == 1) #expect( dispatchedEvents.first?.id == nil, - "Event's ID field should be nil because the raw ID field contained NUL.") + "Event's ID field should be nil because the raw ID field contained NUL." + ) #expect(dispatchedEvents.first?.data == "test") } @@ -120,11 +122,13 @@ struct ParserTests { // After "id: " line, lastEventId should be reset to empty string #expect( await parser.getLastEventId() == "", - "lastEventID should be reset to empty string by an 'id:' line.") + "lastEventID should be reset to empty string by an 'id:' line." + ) #expect( events[1].id == "", // Changed from nil to empty string - "Second event's currentEventId should be empty string as 'id:' resets it.") + "Second event's currentEventId should be empty string as 'id:' resets it." + ) #expect(events[1].data == "event2") } @@ -157,7 +161,8 @@ struct ParserTests { #expect(events.first?.data == "test data") #expect( events.first?.retry == 5000, - "The retry value should be part of the event if dispatched with it.") + "The retry value should be part of the event if dispatched with it." + ) } @Test("Retry field only updates reconnection time") @@ -170,7 +175,8 @@ struct ParserTests { #expect( events.isEmpty, - "A message containing only a 'retry' field should not produce an event.") + "A message containing only a 'retry' field should not produce an event." + ) #expect(await parser.getReconnectionTime() == 1234) #expect(initialReconnectionTime != 1234) } @@ -294,7 +300,8 @@ struct ParserTests { #expect(events.count == 1) #expect( events.first?.data == "test\u{FFFD}string", - "Invalid UTF-8 byte should be replaced by replacement character.") + "Invalid UTF-8 byte should be replaced by replacement character." + ) } @Test("Field without colon is ignored") @@ -387,13 +394,16 @@ struct ParserTests { #expect(events.first?.event == "", "Event type from 'event' line without value.") #expect( events.first?.id == "", // Changed from nil to empty string - "currentEventId should be empty string as 'id' line had no value.") + "currentEventId should be empty string as 'id' line had no value." + ) #expect( await parser.getLastEventId() == "", - "LastEventId should be empty string from 'id' line without value.") + "LastEventId should be empty string from 'id' line without value." + ) #expect( await parser.getReconnectionTime() == initialReconnectTime, - "Empty 'retry' field should not change reconnection time.") + "Empty 'retry' field should not change reconnection time." + ) } } @@ -401,7 +411,8 @@ struct ParserTests { struct CommentAndLineTests { @Test("Only comment lines and empty lines produce no events") func testOnlyCommentLinesAndEmptyLinesProduceNoEvents() async { - let stream = ":comment\n\n:another comment\r\n\r\n" // Includes dispatch-triggering empty lines but no data fields + // Includes dispatch-triggering empty lines but no data fields + let stream = ":comment\n\n:another comment\r\n\r\n" let events = await getEvents(from: stream) #expect( events.isEmpty, @@ -463,7 +474,9 @@ struct ParserTests { let stream = "data: final event" // No trailing newline or blank line let events = await getEvents(from: stream) #expect( - events.count == 1, "Event should be dispatched based on current finish() logic.") + events.count == 1, + "Event should be dispatched based on current finish() logic." + ) #expect(events.first?.data == "final event") let streamWithID = "id: lastid\ndata: final event with id" @@ -526,7 +539,8 @@ struct ParserTests { fullStreamBytes.append( contentsOf: createSSEMessage(fields: [ (name: "data", value: "test with bom") - ])) + ]) + ) let parser = EventSource.Parser() // Process each byte individually