import Testing
@testable import WebTerm

/// T-iOS-15 · Fan-out adapter tests. `SessionEngine.events` is a
/// single-consumer `AsyncStream`, but TerminalViewModel, GateViewModel AND the
/// session-activity bridge must all observe it — `EventFanOut` is the wiring
/// layer's broadcast seam. These tests pin its contract: every branch receives
/// every element, in order, and source termination / cancel() propagate.
@Suite("EventFanOut")
struct EventFanOutTests {
    private static let branchCount = 3

    @Test("每个分支都按序收到全部元素")
    func allBranchesReceiveAllElementsInOrder() async {
        // Arrange
        let (source, continuation) = AsyncStream<Int>.makeStream()
        let fanOut = EventFanOut(source: source, branchCount: Self.branchCount)

        // Act
        for value in 1...5 {
            continuation.yield(value)
        }
        continuation.finish()

        // Assert: unbounded buffering — late consumption still sees everything.
        for branch in fanOut.branches {
            var received: [Int] = []
            for await value in branch {
                received.append(value)
            }
            #expect(received == [1, 2, 3, 4, 5])
        }
    }

    @Test("分支数量与请求一致")
    func branchCountMatchesRequest() {
        let (source, _) = AsyncStream<Int>.makeStream()
        let fanOut = EventFanOut(source: source, branchCount: Self.branchCount)
        #expect(fanOut.branches.count == Self.branchCount)
    }

    @Test("源结束 → 所有分支结束（消费循环退出，不悬挂）")
    func sourceFinishFinishesEveryBranch() async {
        // Arrange
        let (source, continuation) = AsyncStream<String>.makeStream()
        let fanOut = EventFanOut(source: source, branchCount: Self.branchCount)

        // Act
        continuation.yield("only")
        continuation.finish()

        // Assert: every branch loop terminates after draining.
        for branch in fanOut.branches {
            var count = 0
            for await _ in branch {
                count += 1
            }
            #expect(count == 1)
        }
    }

    @Test("cancel() → 分支立即终止（teardown 不泄漏消费任务）")
    func cancelTerminatesBranches() async {
        // Arrange
        let (source, continuation) = AsyncStream<Int>.makeStream()
        let fanOut = EventFanOut(source: source, branchCount: Self.branchCount)

        // Act: cancel without ever finishing the source.
        fanOut.cancel()
        continuation.yield(42) // post-cancel input must not hang consumers

        // Assert: iteration completes (content is unspecified mid-flight —
        // termination is the contract under test).
        for branch in fanOut.branches {
            for await _ in branch {}
        }
        #expect(Bool(true)) // reaching here = no hang
    }
}
