88} from "#koko/composables/terminal/protocol" ;
99import { useKokoTerminalHeartbeat } from "#koko/composables/terminal/useTerminalHeartbeat" ;
1010import { useKokoTerminalTransport } from "#koko/composables/terminal/useTerminalTransport" ;
11+ import { normalizeConnectionFailure } from "~/utils/connectionFailure" ;
12+ import terminalSocketSource from "../../composables/terminal/useTerminalSocket.ts?raw" ;
1113
1214afterEach ( ( ) => {
1315 vi . useRealTimers ( ) ;
@@ -30,21 +32,28 @@ it("distinguishes Koko session end, Koko timeout, offline and unexplained closes
3032 expect ( describeTerminalClose ( abnormal , "" , true ) . source ) . toBe ( "koko" ) ;
3133} ) ;
3234
33- it ( "uses raw startup output for an early close and clears it once ready" , ( ) => {
35+ it ( "uses sanitized startup bytes for an early close and clears them once ready" , ( ) => {
3436 const capture = createKokoStartupOutputCapture ( ) ;
35- const encoded = new TextEncoder ( ) . encode ( "sqlserver: 连接被拒绝" ) ;
37+ const fallback = "Koko 已结束会话:资产连接已结束" ;
38+ const encoded = new TextEncoder ( ) . encode ( "\u001b[31m认证失败\u001b[0m" ) ;
39+ const prefixBytes = new TextEncoder ( ) . encode ( "\u001b[31m" ) . length ;
40+ const splitAt = prefixBytes + 1 ;
3641
37- capture . append ( encoded . slice ( 0 , 13 ) ) ;
38- capture . append ( encoded . slice ( 13 ) ) ;
39- expect ( resolveKokoTerminalCloseMessage ( capture . take ( ) , "Koko 已结束会话:资产连接已结束" ) ) . toBe (
40- "sqlserver: 连接被拒绝"
42+ expect ( new TextEncoder ( ) . encode ( "认" ) . length ) . toBeGreaterThan ( 1 ) ;
43+ capture . append ( encoded . slice ( 0 , splitAt ) ) ;
44+ capture . append ( encoded . slice ( splitAt ) ) ;
45+ const startup = normalizeConnectionFailure ( capture . take ( ) ) ;
46+
47+ expect ( startup ) . toBe ( "认证失败" ) ;
48+ expect ( normalizeConnectionFailure ( "开始连接\r\nssh: handshake failed: EOF" ) ) . toBe (
49+ "开始连接\nssh: handshake failed: EOF"
4150 ) ;
51+ expect ( startup ) . not . toContain ( "\u001b" ) ;
52+ expect ( resolveKokoTerminalCloseMessage ( startup , fallback ) ) . toBe ( "认证失败" ) ;
4253
4354 capture . markReady ( ) ;
4455 capture . append ( new TextEncoder ( ) . encode ( "must not replace the close reason" ) ) ;
45- expect ( resolveKokoTerminalCloseMessage ( capture . take ( ) , "Koko 已结束会话:资产连接已结束" ) ) . toBe (
46- "Koko 已结束会话:资产连接已结束"
47- ) ;
56+ expect ( resolveKokoTerminalCloseMessage ( normalizeConnectionFailure ( capture . take ( ) ) , fallback ) ) . toBe ( fallback ) ;
4857} ) ;
4958
5059it ( "keeps only the last 8 KiB of startup output" , ( ) => {
@@ -58,6 +67,14 @@ it("keeps only the last 8 KiB of startup output", () => {
5867 expect ( capture . take ( ) ) . toBe ( `${ "x" . repeat ( limit - 4 ) } tail` ) ;
5968} ) ;
6069
70+ it ( "reads sanitized startup bytes on close instead of the xterm buffer" , ( ) => {
71+ expect ( terminalSocketSource ) . toContain ( "normalizeConnectionFailure(startupOutput.take())" ) ;
72+ expect ( terminalSocketSource ) . toContain ( "socketOpened = true" ) ;
73+ expect ( terminalSocketSource ) . toContain ( "{ dismissible: socketOpened }" ) ;
74+ expect ( terminalSocketSource ) . toContain ( "onTerminalReady: startupOutput.markReady" ) ;
75+ expect ( terminalSocketSource ) . not . toContain ( "getXTerminalLineContent" ) ;
76+ } ) ;
77+
6178it ( "sends heartbeats only on open sockets, updates send time and stops after close" , ( ) => {
6279 vi . useFakeTimers ( ) ;
6380 const socket = { readyState : WebSocket . CONNECTING as number , send : vi . fn ( ) } ;
0 commit comments