<!-- task=kotlin_wss_crosstest plan=0 tag=KOTLIN_WSS_CROSSTEST -->

# Kotlin Server WSS Crosstest 추가

## 이 파일을 읽는 구현 에이전트에게

각 항목의 체크리스트를 완료 후 `[x]`로 표시한다.
중간 검증 명령을 실제로 실행하고 출력을 `CODE_REVIEW.md`의 검증 결과 섹션에 붙여 넣는다.
계획과 다르게 구현한 부분은 반드시 `CODE_REVIEW.md`의 "계획 대비 변경 사항"에 이유와 함께 기록한다.

## 배경

`kotlin_wss_tls` task에서 `WsServer`에 `sslContext` 파라미터를 추가해 WSS를 지원하게 됐다.
그러나 Kotlin server orchestrator 4개(`kotlin_dart.kt`, `kotlin_go.kt`, `kotlin_python.kt`, `kotlin_typescript.kt`)는
TLS TCP phase만 추가됐고 WSS phase는 누락된 상태다.
4개 client helper(`dart_kotlin_client.dart`, `go_kotlin_client Main.kt`, `python_kotlin_client.py`, `typescript_kotlin_client.ts`)는
모두 `"wss"` case가 이미 구현되어 있으므로 orchestrator쪽만 수정하면 된다.

## 포트 배정

| 파일 | TCP | WS | TLS TCP | WSS (신규) |
|------|-----|----|---------|-----------|
| kotlin_dart.kt | 29490 | 29492 | 29494 | **29496** |
| kotlin_go.kt | 29390 | 29392 | 29394 | **29396** |
| kotlin_python.kt | 29394 | 29396 | 29398 | **29400** |
| kotlin_typescript.kt | 29794 | 29796 | 29798 | **29800** |

각 WSS 포트는 해당 파일 내 기존 포트와 겹치지 않는다. 타 파일과 동일 포트가 있어도 orchestrator는 순차 실행이므로 충돌 없음.

---

## [KOTLIN_WSS_CROSSTEST-1] kotlin_dart.kt — WSS phase 추가

### 문제

`kotlin/crosstest/kotlin_dart.kt`의 `main()`이 `runTlsTcp()` 호출 후 바로 PASS를 출력한다.
WSS (kotlin WsServer + dart `"wss"` client) phase가 없다.

### 해결 방법

1. `WSS_PORT = 29496` 상수 추가 (line 37 다음)
2. `main()`에 `runWss()` 호출 뒤 `runTlsTcp()` 전에 `runWss()` 사이에 `runWss()` 실행 후 WSS phase를 호출한다.
   정확히는 `runTlsTcp()` 다음, PASS 출력 전에 `runWss()` 대신 별도 `runWss()` WSS 함수를 추가한다.
3. `runWss()`는 기존 WS 패턴과 동일하게 단일 `WsServer` 인스턴스를 유지하고 `sslContext = serverCtx`를 전달한다.
4. `runWssSendPush` / `runWssRequests` 2개 내부 함수 추가.

#### Before (kotlin_dart.kt:42-57)

```kotlin
fun main() = runBlocking {
    println("INFO typeName kotlin=${typeNameOf<TestData>()}")
    try {
        runTcpSendPush()
        delay(150)
        runTcpRequests()
        delay(150)
        runWs()
        delay(150)
        runTlsTcp()
        println("PASS all kotlin-server/dart-client crosstests passed")
    } catch (error: Throwable) {
        System.err.println("FAIL crosstest error=${error.message ?: error}")
        exitProcess(1)
    }
}
```

#### After

```kotlin
fun main() = runBlocking {
    println("INFO typeName kotlin=${typeNameOf<TestData>()}")
    try {
        runTcpSendPush()
        delay(150)
        runTcpRequests()
        delay(150)
        runWs()
        delay(150)
        runTlsTcp()
        delay(150)
        runWss()
        println("PASS all kotlin-server/dart-client crosstests passed")
    } catch (error: Throwable) {
        System.err.println("FAIL crosstest error=${error.message ?: error}")
        exitProcess(1)
    }
}
```

#### Before (kotlin_dart.kt:37)

```kotlin
private const val TLS_TCP_PORT = 29494
```

#### After

```kotlin
private const val TLS_TCP_PORT = 29494
private const val WSS_PORT = 29496
```

#### 추가할 함수 (runTlsTcp() 이후에 삽입)

```kotlin
private suspend fun runWss() = coroutineScope {
    val certFile = kotlinResourceFile("server.crt")
    val keyFile = kotlinResourceFile("server.key")
    val serverCtx = buildServerSslContext(certFile.path, keyFile.path)
    val server = WsServer(HOST, WSS_PORT, WS_PATH, sslContext = serverCtx) { conn ->
        WsClient.forServer(conn, 0, 0, parserMap())
    }
    server.start()
    delay(100)
    try {
        runWssSendPush(server, certFile.path)
        delay(150)
        runWssRequests(server, certFile.path)
    } finally {
        server.stop()
    }
}

private suspend fun runWssSendPush(server: WsServer, certPath: String) = coroutineScope {
    val received = CompletableDeferred<Boolean>()
    server.onClientConnected = { client ->
        addListenerTyped<TestData>(client.communicator) { data ->
            println("SERVER_RECEIVED index=${data.index} message=${data.message}")
            val valid = data.index == 101 && data.message == "fire from dart client"
            received.complete(valid)
            if (valid) {
                launch {
                    client.send(
                        TestData.newBuilder()
                            .setIndex(200)
                            .setMessage("push from kotlin server")
                            .build(),
                    )
                }
            }
        }
    }
    runDartClient("wss", WSS_PORT, "send-push", setOf("1", "2"), certPath)
    val ok = withTimeoutOrNull(SERVER_OBSERVATION_MS) { received.await() } ?: false
    check(ok) { "WSS send-push server did not receive expected data" }
}

private suspend fun runWssRequests(server: WsServer, certPath: String) {
    server.onClientConnected = { client ->
        addRequestListenerTyped<TestData, TestData>(client.communicator) { req ->
            TestData.newBuilder()
                .setIndex(req.index * 2)
                .setMessage("echo: ${req.message}")
                .build()
        }
    }
    runDartClient("wss", WSS_PORT, "requests", setOf("3", "4"), certPath)
}
```

### 수정 파일 및 체크리스트

- [x] `kotlin/crosstest/kotlin_dart.kt`
  - [x] `WSS_PORT = 29496` 상수 추가
  - [x] `main()`에 `delay(150)` + `runWss()` 추가 (runTlsTcp() 뒤)
  - [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가

### 테스트 작성

신규 파일 불필요. orchestrator 자체가 e2e 검증이다.

### 중간 검증

```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```

---

## [KOTLIN_WSS_CROSSTEST-2] kotlin_go.kt — WSS phase 추가

### 문제

`kotlin/crosstest/kotlin_go.kt`에 WSS phase 없음. `go_kotlin_client` Main.kt에는 `"wss"` case 구현됨.

### 해결 방법

`kotlin_dart.kt`와 동일한 패턴. 차이점:
- `WSS_PORT = 29396`
- 클라이언트 runner: `runGoClient("wss", WSS_PORT, ...)` (이미 존재하는 함수)

#### Before (kotlin_go.kt:37)

```kotlin
private const val TLS_TCP_PORT = 29394
```

#### After

```kotlin
private const val TLS_TCP_PORT = 29394
private const val WSS_PORT = 29396
```

`main()` 변경, `runWss()` / `runWssSendPush()` / `runWssRequests()` 추가는 kotlin_dart.kt와 동일 패턴.
`runGoClient("wss", ...)` 호출.

### 수정 파일 및 체크리스트

- [x] `kotlin/crosstest/kotlin_go.kt`
  - [x] `WSS_PORT = 29396` 상수 추가
  - [x] `main()`에 `delay(150)` + `runWss()` 추가
  - [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (Go client 호출)

### 테스트 작성

orchestrator e2e로 충분.

### 중간 검증

```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```

---

## [KOTLIN_WSS_CROSSTEST-3] kotlin_python.kt — WSS phase 추가

### 문제

`kotlin/crosstest/kotlin_python.kt`에 WSS phase 없음. `python_kotlin_client.py`에는 `"wss"` case 구현됨.

### 해결 방법

동일 패턴. 차이점:
- `WSS_PORT = 29400`
- 클라이언트 runner: `runPythonClient("wss", WSS_PORT, ...)`

#### Before (kotlin_python.kt:37)

```kotlin
private const val TLS_TCP_PORT = 29398
```

#### After

```kotlin
private const val TLS_TCP_PORT = 29398
private const val WSS_PORT = 29400
```

### 수정 파일 및 체크리스트

- [x] `kotlin/crosstest/kotlin_python.kt`
  - [x] `WSS_PORT = 29400` 상수 추가
  - [x] `main()`에 `delay(150)` + `runWss()` 추가
  - [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (Python client 호출)

### 테스트 작성

orchestrator e2e로 충분.

### 중간 검증

```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```

---

## [KOTLIN_WSS_CROSSTEST-4] kotlin_typescript.kt — WSS phase 추가

### 문제

`kotlin/crosstest/kotlin_typescript.kt`에 WSS phase 없음. `typescript_kotlin_client.ts`에는 `"wss"` case 구현됨.

### 해결 방법

동일 패턴. 차이점:
- `WSS_PORT = 29800`
- 클라이언트 runner: `runTypescriptClient("wss", WSS_PORT, ...)`

#### Before (kotlin_typescript.kt:37)

```kotlin
private const val TLS_TCP_PORT = 29798
```

#### After

```kotlin
private const val TLS_TCP_PORT = 29798
private const val WSS_PORT = 29800
```

### 수정 파일 및 체크리스트

- [x] `kotlin/crosstest/kotlin_typescript.kt`
  - [x] `WSS_PORT = 29800` 상수 추가
  - [x] `main()`에 `delay(150)` + `runWss()` 추가
  - [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (TypeScript client 호출)

### 테스트 작성

orchestrator e2e로 충분.

### 중간 검증

```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```

---

## 의존 관계 및 구현 순서

1-4번은 서로 독립적이나 중간 검증을 공유하므로 4개 모두 수정 후 한 번에 컴파일 검증.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `kotlin/crosstest/kotlin_dart.kt` | KOTLIN_WSS_CROSSTEST-1 |
| `kotlin/crosstest/kotlin_go.kt` | KOTLIN_WSS_CROSSTEST-2 |
| `kotlin/crosstest/kotlin_python.kt` | KOTLIN_WSS_CROSSTEST-3 |
| `kotlin/crosstest/kotlin_typescript.kt` | KOTLIN_WSS_CROSSTEST-4 |

## 최종 검증

```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinDartKt
# PASS all kotlin-server/dart-client crosstests passed

$ cd kotlin && env PATH=/config/go-sdk/go/bin:$PATH JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 \
  GRADLE_USER_HOME=/tmp/gradle GOCACHE=/tmp/go-build GOMODCACHE=/tmp/go-mod \
  ./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinGoKt
# PASS all kotlin-server/go-client crosstests passed

$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinPythonKt
# PASS all kotlin-server/python-client crosstests passed

$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
  ./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinTypescriptKt
# PASS all kotlin-server/typescript-client crosstests passed
```
