feat(ports,ipc): list/kill/sessions protocol surface
- commit
- 36aa808b5649c50d6d7b263ada7f700adc09efb5
- author
- brice · · 2026-07-02 11:23 UTC
- parent
- 58001c6
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +219 -0
M internal/adapters/ipc/protocol.go +117 -0
| @@ -90,6 +90,29 @@ type Detached struct { | |||
| 90 | 90 | Reason uint8 | |
| 91 | 91 | } | |
| 92 | 92 | ||
| 93 | + | // List asks the daemon to enumerate its live sessions. It carries no | |
| 94 | + | // fields; the request is fully described by its message type. | |
| 95 | + | type List struct{} | |
| 96 | + | ||
| 97 | + | // Kill asks the daemon to terminate the named session. | |
| 98 | + | type Kill struct { | |
| 99 | + | Name string | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // SessionInfo describes one session in a Sessions listing. | |
| 103 | + | type SessionInfo struct { | |
| 104 | + | SessionID string | |
| 105 | + | Name string | |
| 106 | + | Ephemeral bool | |
| 107 | + | Windows uint16 | |
| 108 | + | Attached bool | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | // Sessions is the daemon's reply to a List, enumerating live sessions. | |
| 112 | + | type Sessions struct { | |
| 113 | + | Sessions []SessionInfo | |
| 114 | + | } | |
| 115 | + | ||
| 93 | 116 | // payloadWriter builds a message payload by appending fields in wire order. | |
| 94 | 117 | type payloadWriter struct { | |
| 95 | 118 | b []byte | |
| @@ -378,3 +401,97 @@ func UnmarshalDetached(b []byte) (Detached, error) { | |||
| 378 | 401 | } | |
| 379 | 402 | return Detached{Reason: reason}, nil | |
| 380 | 403 | } | |
| 404 | + | ||
| 405 | + | // MarshalList encodes a List message payload (always empty). | |
| 406 | + | func MarshalList(List) []byte { | |
| 407 | + | return nil | |
| 408 | + | } | |
| 409 | + | ||
| 410 | + | // UnmarshalList decodes a List message payload. | |
| 411 | + | func UnmarshalList(b []byte) (List, error) { | |
| 412 | + | r := payloadReader{b: b} | |
| 413 | + | if err := r.done(); err != nil { | |
| 414 | + | return List{}, err | |
| 415 | + | } | |
| 416 | + | return List{}, nil | |
| 417 | + | } | |
| 418 | + | ||
| 419 | + | // MarshalKill encodes m into a Kill message payload. | |
| 420 | + | func MarshalKill(m Kill) []byte { | |
| 421 | + | w := payloadWriter{} | |
| 422 | + | w.putString(m.Name) | |
| 423 | + | return w.b | |
| 424 | + | } | |
| 425 | + | ||
| 426 | + | // UnmarshalKill decodes a Kill message payload. | |
| 427 | + | func UnmarshalKill(b []byte) (Kill, error) { | |
| 428 | + | r := payloadReader{b: b} | |
| 429 | + | name, err := r.getString() | |
| 430 | + | if err != nil { | |
| 431 | + | return Kill{}, err | |
| 432 | + | } | |
| 433 | + | if err := r.done(); err != nil { | |
| 434 | + | return Kill{}, err | |
| 435 | + | } | |
| 436 | + | return Kill{Name: name}, nil | |
| 437 | + | } | |
| 438 | + | ||
| 439 | + | // MarshalSessions encodes m into a Sessions message payload: a uint16 count | |
| 440 | + | // followed by that many session records. | |
| 441 | + | func MarshalSessions(m Sessions) []byte { | |
| 442 | + | w := payloadWriter{} | |
| 443 | + | w.putUint16(uint16(len(m.Sessions))) | |
| 444 | + | for _, s := range m.Sessions { | |
| 445 | + | w.putString(s.SessionID) | |
| 446 | + | w.putString(s.Name) | |
| 447 | + | if s.Ephemeral { | |
| 448 | + | w.putUint8(1) | |
| 449 | + | } else { | |
| 450 | + | w.putUint8(0) | |
| 451 | + | } | |
| 452 | + | w.putUint16(s.Windows) | |
| 453 | + | if s.Attached { | |
| 454 | + | w.putUint8(1) | |
| 455 | + | } else { | |
| 456 | + | w.putUint8(0) | |
| 457 | + | } | |
| 458 | + | } | |
| 459 | + | return w.b | |
| 460 | + | } | |
| 461 | + | ||
| 462 | + | // UnmarshalSessions decodes a Sessions message payload. | |
| 463 | + | func UnmarshalSessions(b []byte) (Sessions, error) { | |
| 464 | + | r := payloadReader{b: b} | |
| 465 | + | count, err := r.getUint16() | |
| 466 | + | if err != nil { | |
| 467 | + | return Sessions{}, err | |
| 468 | + | } | |
| 469 | + | sessions := make([]SessionInfo, 0, count) | |
| 470 | + | for i := 0; i < int(count); i++ { | |
| 471 | + | var s SessionInfo | |
| 472 | + | if s.SessionID, err = r.getString(); err != nil { | |
| 473 | + | return Sessions{}, err | |
| 474 | + | } | |
| 475 | + | if s.Name, err = r.getString(); err != nil { | |
| 476 | + | return Sessions{}, err | |
| 477 | + | } | |
| 478 | + | eph, err := r.getUint8() | |
| 479 | + | if err != nil { | |
| 480 | + | return Sessions{}, err | |
| 481 | + | } | |
| 482 | + | s.Ephemeral = eph != 0 | |
| 483 | + | if s.Windows, err = r.getUint16(); err != nil { | |
| 484 | + | return Sessions{}, err | |
| 485 | + | } | |
| 486 | + | att, err := r.getUint8() | |
| 487 | + | if err != nil { | |
| 488 | + | return Sessions{}, err | |
| 489 | + | } | |
| 490 | + | s.Attached = att != 0 | |
| 491 | + | sessions = append(sessions, s) | |
| 492 | + | } | |
| 493 | + | if err := r.done(); err != nil { | |
| 494 | + | return Sessions{}, err | |
| 495 | + | } | |
| 496 | + | return Sessions{Sessions: sessions}, nil | |
| 497 | + | } | |
| @@ -90,6 +90,29 @@ type Detached struct { | |||||
| 90 | Reason uint8 | 90 | Reason uint8 | ||
| 91 | } | 91 | } | ||
| 92 | 92 | ||||
| 93 | + | // List asks the daemon to enumerate its live sessions. It carries no | |||
| 94 | + | // fields; the request is fully described by its message type. | |||
| 95 | + | type List struct{} | |||
| 96 | + | ||||
| 97 | + | // Kill asks the daemon to terminate the named session. | |||
| 98 | + | type Kill struct { | |||
| 99 | + | Name string | |||
| 100 | + | } | |||
| 101 | + | ||||
| 102 | + | // SessionInfo describes one session in a Sessions listing. | |||
| 103 | + | type SessionInfo struct { | |||
| 104 | + | SessionID string | |||
| 105 | + | Name string | |||
| 106 | + | Ephemeral bool | |||
| 107 | + | Windows uint16 | |||
| 108 | + | Attached bool | |||
| 109 | + | } | |||
| 110 | + | ||||
| 111 | + | // Sessions is the daemon's reply to a List, enumerating live sessions. | |||
| 112 | + | type Sessions struct { | |||
| 113 | + | Sessions []SessionInfo | |||
| 114 | + | } | |||
| 115 | + | ||||
| 93 | // payloadWriter builds a message payload by appending fields in wire order. | 116 | // payloadWriter builds a message payload by appending fields in wire order. | ||
| 94 | type payloadWriter struct { | 117 | type payloadWriter struct { | ||
| 95 | b []byte | 118 | b []byte | ||
| @@ -378,3 +401,97 @@ func UnmarshalDetached(b []byte) (Detached, error) { | |||||
| 378 | } | 401 | } | ||
| 379 | return Detached{Reason: reason}, nil | 402 | return Detached{Reason: reason}, nil | ||
| 380 | } | 403 | } | ||
| 404 | + | ||||
| 405 | + | // MarshalList encodes a List message payload (always empty). | |||
| 406 | + | func MarshalList(List) []byte { | |||
| 407 | + | return nil | |||
| 408 | + | } | |||
| 409 | + | ||||
| 410 | + | // UnmarshalList decodes a List message payload. | |||
| 411 | + | func UnmarshalList(b []byte) (List, error) { | |||
| 412 | + | r := payloadReader{b: b} | |||
| 413 | + | if err := r.done(); err != nil { | |||
| 414 | + | return List{}, err | |||
| 415 | + | } | |||
| 416 | + | return List{}, nil | |||
| 417 | + | } | |||
| 418 | + | ||||
| 419 | + | // MarshalKill encodes m into a Kill message payload. | |||
| 420 | + | func MarshalKill(m Kill) []byte { | |||
| 421 | + | w := payloadWriter{} | |||
| 422 | + | w.putString(m.Name) | |||
| 423 | + | return w.b | |||
| 424 | + | } | |||
| 425 | + | ||||
| 426 | + | // UnmarshalKill decodes a Kill message payload. | |||
| 427 | + | func UnmarshalKill(b []byte) (Kill, error) { | |||
| 428 | + | r := payloadReader{b: b} | |||
| 429 | + | name, err := r.getString() | |||
| 430 | + | if err != nil { | |||
| 431 | + | return Kill{}, err | |||
| 432 | + | } | |||
| 433 | + | if err := r.done(); err != nil { | |||
| 434 | + | return Kill{}, err | |||
| 435 | + | } | |||
| 436 | + | return Kill{Name: name}, nil | |||
| 437 | + | } | |||
| 438 | + | ||||
| 439 | + | // MarshalSessions encodes m into a Sessions message payload: a uint16 count | |||
| 440 | + | // followed by that many session records. | |||
| 441 | + | func MarshalSessions(m Sessions) []byte { | |||
| 442 | + | w := payloadWriter{} | |||
| 443 | + | w.putUint16(uint16(len(m.Sessions))) | |||
| 444 | + | for _, s := range m.Sessions { | |||
| 445 | + | w.putString(s.SessionID) | |||
| 446 | + | w.putString(s.Name) | |||
| 447 | + | if s.Ephemeral { | |||
| 448 | + | w.putUint8(1) | |||
| 449 | + | } else { | |||
| 450 | + | w.putUint8(0) | |||
| 451 | + | } | |||
| 452 | + | w.putUint16(s.Windows) | |||
| 453 | + | if s.Attached { | |||
| 454 | + | w.putUint8(1) | |||
| 455 | + | } else { | |||
| 456 | + | w.putUint8(0) | |||
| 457 | + | } | |||
| 458 | + | } | |||
| 459 | + | return w.b | |||
| 460 | + | } | |||
| 461 | + | ||||
| 462 | + | // UnmarshalSessions decodes a Sessions message payload. | |||
| 463 | + | func UnmarshalSessions(b []byte) (Sessions, error) { | |||
| 464 | + | r := payloadReader{b: b} | |||
| 465 | + | count, err := r.getUint16() | |||
| 466 | + | if err != nil { | |||
| 467 | + | return Sessions{}, err | |||
| 468 | + | } | |||
| 469 | + | sessions := make([]SessionInfo, 0, count) | |||
| 470 | + | for i := 0; i < int(count); i++ { | |||
| 471 | + | var s SessionInfo | |||
| 472 | + | if s.SessionID, err = r.getString(); err != nil { | |||
| 473 | + | return Sessions{}, err | |||
| 474 | + | } | |||
| 475 | + | if s.Name, err = r.getString(); err != nil { | |||
| 476 | + | return Sessions{}, err | |||
| 477 | + | } | |||
| 478 | + | eph, err := r.getUint8() | |||
| 479 | + | if err != nil { | |||
| 480 | + | return Sessions{}, err | |||
| 481 | + | } | |||
| 482 | + | s.Ephemeral = eph != 0 | |||
| 483 | + | if s.Windows, err = r.getUint16(); err != nil { | |||
| 484 | + | return Sessions{}, err | |||
| 485 | + | } | |||
| 486 | + | att, err := r.getUint8() | |||
| 487 | + | if err != nil { | |||
| 488 | + | return Sessions{}, err | |||
| 489 | + | } | |||
| 490 | + | s.Attached = att != 0 | |||
| 491 | + | sessions = append(sessions, s) | |||
| 492 | + | } | |||
| 493 | + | if err := r.done(); err != nil { | |||
| 494 | + | return Sessions{}, err | |||
| 495 | + | } | |||
| 496 | + | return Sessions{Sessions: sessions}, nil | |||
| 497 | + | } | |||
M internal/adapters/ipc/protocol_test.go +96 -0
| @@ -299,6 +299,102 @@ func TestDetachedGoldenAndRoundTrip(t *testing.T) { | |||
| 299 | 299 | assertTrailingGarbageFails(t, got, UnmarshalDetached) | |
| 300 | 300 | } | |
| 301 | 301 | ||
| 302 | + | func TestListGoldenAndRoundTrip(t *testing.T) { | |
| 303 | + | got := MarshalList(List{}) | |
| 304 | + | if len(got) != 0 { | |
| 305 | + | t.Fatalf("MarshalList() = %#v, want empty", got) | |
| 306 | + | } | |
| 307 | + | back, err := UnmarshalList(got) | |
| 308 | + | if err != nil { | |
| 309 | + | t.Fatalf("UnmarshalList() error = %v", err) | |
| 310 | + | } | |
| 311 | + | if back != (List{}) { | |
| 312 | + | t.Fatalf("round trip = %#v, want %#v", back, List{}) | |
| 313 | + | } | |
| 314 | + | assertTrailingGarbageFails(t, got, UnmarshalList) | |
| 315 | + | } | |
| 316 | + | ||
| 317 | + | func TestKillGoldenAndRoundTrip(t *testing.T) { | |
| 318 | + | tests := []struct { | |
| 319 | + | name string | |
| 320 | + | msg Kill | |
| 321 | + | want []byte | |
| 322 | + | }{ | |
| 323 | + | {name: "named", msg: Kill{Name: "main"}, want: []byte{0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e}}, | |
| 324 | + | {name: "empty", msg: Kill{Name: ""}, want: []byte{0x00, 0x00}}, | |
| 325 | + | } | |
| 326 | + | ||
| 327 | + | for _, tt := range tests { | |
| 328 | + | t.Run(tt.name, func(t *testing.T) { | |
| 329 | + | got := MarshalKill(tt.msg) | |
| 330 | + | if !bytes.Equal(got, tt.want) { | |
| 331 | + | t.Fatalf("MarshalKill() = %#v, want %#v", got, tt.want) | |
| 332 | + | } | |
| 333 | + | back, err := UnmarshalKill(got) | |
| 334 | + | if err != nil { | |
| 335 | + | t.Fatalf("UnmarshalKill() error = %v", err) | |
| 336 | + | } | |
| 337 | + | if !reflect.DeepEqual(back, tt.msg) { | |
| 338 | + | t.Fatalf("round trip = %#v, want %#v", back, tt.msg) | |
| 339 | + | } | |
| 340 | + | }) | |
| 341 | + | } | |
| 342 | + | ||
| 343 | + | full := MarshalKill(tests[0].msg) | |
| 344 | + | assertAllPrefixesFail(t, full, UnmarshalKill) | |
| 345 | + | assertTrailingGarbageFails(t, full, UnmarshalKill) | |
| 346 | + | } | |
| 347 | + | ||
| 348 | + | func TestSessionsGoldenAndRoundTrip(t *testing.T) { | |
| 349 | + | tests := []struct { | |
| 350 | + | name string | |
| 351 | + | msg Sessions | |
| 352 | + | want []byte | |
| 353 | + | }{ | |
| 354 | + | { | |
| 355 | + | name: "empty", | |
| 356 | + | msg: Sessions{}, | |
| 357 | + | want: []byte{0x00, 0x00}, | |
| 358 | + | }, | |
| 359 | + | { | |
| 360 | + | name: "two", | |
| 361 | + | msg: Sessions{Sessions: []SessionInfo{ | |
| 362 | + | {SessionID: "0", Name: "0", Ephemeral: true, Windows: 1, Attached: false}, | |
| 363 | + | {SessionID: "work", Name: "proj", Ephemeral: false, Windows: 5, Attached: true}, | |
| 364 | + | }}, | |
| 365 | + | want: []byte{ | |
| 366 | + | 0x00, 0x02, | |
| 367 | + | 0x00, 0x01, 0x30, 0x00, 0x01, 0x30, 0x01, 0x00, 0x01, 0x00, | |
| 368 | + | 0x00, 0x04, 0x77, 0x6f, 0x72, 0x6b, 0x00, 0x04, 0x70, 0x72, 0x6f, 0x6a, 0x00, 0x00, 0x05, 0x01, | |
| 369 | + | }, | |
| 370 | + | }, | |
| 371 | + | } | |
| 372 | + | ||
| 373 | + | for _, tt := range tests { | |
| 374 | + | t.Run(tt.name, func(t *testing.T) { | |
| 375 | + | got := MarshalSessions(tt.msg) | |
| 376 | + | if !bytes.Equal(got, tt.want) { | |
| 377 | + | t.Fatalf("MarshalSessions() = %#v, want %#v", got, tt.want) | |
| 378 | + | } | |
| 379 | + | back, err := UnmarshalSessions(got) | |
| 380 | + | if err != nil { | |
| 381 | + | t.Fatalf("UnmarshalSessions() error = %v", err) | |
| 382 | + | } | |
| 383 | + | // Normalize nil vs empty slice for comparison. | |
| 384 | + | if len(back.Sessions) == 0 && len(tt.msg.Sessions) == 0 { | |
| 385 | + | return | |
| 386 | + | } | |
| 387 | + | if !reflect.DeepEqual(back, tt.msg) { | |
| 388 | + | t.Fatalf("round trip = %#v, want %#v", back, tt.msg) | |
| 389 | + | } | |
| 390 | + | }) | |
| 391 | + | } | |
| 392 | + | ||
| 393 | + | full := MarshalSessions(tests[1].msg) | |
| 394 | + | assertAllPrefixesFail(t, full, UnmarshalSessions) | |
| 395 | + | assertTrailingGarbageFails(t, full, UnmarshalSessions) | |
| 396 | + | } | |
| 397 | + | ||
| 302 | 398 | // TestMsgTypeConstantsDistinct guards the enumerations defined in this | |
| 303 | 399 | // package against accidental collisions (Intent, ErrorMsg code, Detached | |
| 304 | 400 | // reason are each independent small spaces, but duplicates within one | |
| @@ -299,6 +299,102 @@ func TestDetachedGoldenAndRoundTrip(t *testing.T) { | |||||
| 299 | assertTrailingGarbageFails(t, got, UnmarshalDetached) | 299 | assertTrailingGarbageFails(t, got, UnmarshalDetached) | ||
| 300 | } | 300 | } | ||
| 301 | 301 | ||||
| 302 | + | func TestListGoldenAndRoundTrip(t *testing.T) { | |||
| 303 | + | got := MarshalList(List{}) | |||
| 304 | + | if len(got) != 0 { | |||
| 305 | + | t.Fatalf("MarshalList() = %#v, want empty", got) | |||
| 306 | + | } | |||
| 307 | + | back, err := UnmarshalList(got) | |||
| 308 | + | if err != nil { | |||
| 309 | + | t.Fatalf("UnmarshalList() error = %v", err) | |||
| 310 | + | } | |||
| 311 | + | if back != (List{}) { | |||
| 312 | + | t.Fatalf("round trip = %#v, want %#v", back, List{}) | |||
| 313 | + | } | |||
| 314 | + | assertTrailingGarbageFails(t, got, UnmarshalList) | |||
| 315 | + | } | |||
| 316 | + | ||||
| 317 | + | func TestKillGoldenAndRoundTrip(t *testing.T) { | |||
| 318 | + | tests := []struct { | |||
| 319 | + | name string | |||
| 320 | + | msg Kill | |||
| 321 | + | want []byte | |||
| 322 | + | }{ | |||
| 323 | + | {name: "named", msg: Kill{Name: "main"}, want: []byte{0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e}}, | |||
| 324 | + | {name: "empty", msg: Kill{Name: ""}, want: []byte{0x00, 0x00}}, | |||
| 325 | + | } | |||
| 326 | + | ||||
| 327 | + | for _, tt := range tests { | |||
| 328 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 329 | + | got := MarshalKill(tt.msg) | |||
| 330 | + | if !bytes.Equal(got, tt.want) { | |||
| 331 | + | t.Fatalf("MarshalKill() = %#v, want %#v", got, tt.want) | |||
| 332 | + | } | |||
| 333 | + | back, err := UnmarshalKill(got) | |||
| 334 | + | if err != nil { | |||
| 335 | + | t.Fatalf("UnmarshalKill() error = %v", err) | |||
| 336 | + | } | |||
| 337 | + | if !reflect.DeepEqual(back, tt.msg) { | |||
| 338 | + | t.Fatalf("round trip = %#v, want %#v", back, tt.msg) | |||
| 339 | + | } | |||
| 340 | + | }) | |||
| 341 | + | } | |||
| 342 | + | ||||
| 343 | + | full := MarshalKill(tests[0].msg) | |||
| 344 | + | assertAllPrefixesFail(t, full, UnmarshalKill) | |||
| 345 | + | assertTrailingGarbageFails(t, full, UnmarshalKill) | |||
| 346 | + | } | |||
| 347 | + | ||||
| 348 | + | func TestSessionsGoldenAndRoundTrip(t *testing.T) { | |||
| 349 | + | tests := []struct { | |||
| 350 | + | name string | |||
| 351 | + | msg Sessions | |||
| 352 | + | want []byte | |||
| 353 | + | }{ | |||
| 354 | + | { | |||
| 355 | + | name: "empty", | |||
| 356 | + | msg: Sessions{}, | |||
| 357 | + | want: []byte{0x00, 0x00}, | |||
| 358 | + | }, | |||
| 359 | + | { | |||
| 360 | + | name: "two", | |||
| 361 | + | msg: Sessions{Sessions: []SessionInfo{ | |||
| 362 | + | {SessionID: "0", Name: "0", Ephemeral: true, Windows: 1, Attached: false}, | |||
| 363 | + | {SessionID: "work", Name: "proj", Ephemeral: false, Windows: 5, Attached: true}, | |||
| 364 | + | }}, | |||
| 365 | + | want: []byte{ | |||
| 366 | + | 0x00, 0x02, | |||
| 367 | + | 0x00, 0x01, 0x30, 0x00, 0x01, 0x30, 0x01, 0x00, 0x01, 0x00, | |||
| 368 | + | 0x00, 0x04, 0x77, 0x6f, 0x72, 0x6b, 0x00, 0x04, 0x70, 0x72, 0x6f, 0x6a, 0x00, 0x00, 0x05, 0x01, | |||
| 369 | + | }, | |||
| 370 | + | }, | |||
| 371 | + | } | |||
| 372 | + | ||||
| 373 | + | for _, tt := range tests { | |||
| 374 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 375 | + | got := MarshalSessions(tt.msg) | |||
| 376 | + | if !bytes.Equal(got, tt.want) { | |||
| 377 | + | t.Fatalf("MarshalSessions() = %#v, want %#v", got, tt.want) | |||
| 378 | + | } | |||
| 379 | + | back, err := UnmarshalSessions(got) | |||
| 380 | + | if err != nil { | |||
| 381 | + | t.Fatalf("UnmarshalSessions() error = %v", err) | |||
| 382 | + | } | |||
| 383 | + | // Normalize nil vs empty slice for comparison. | |||
| 384 | + | if len(back.Sessions) == 0 && len(tt.msg.Sessions) == 0 { | |||
| 385 | + | return | |||
| 386 | + | } | |||
| 387 | + | if !reflect.DeepEqual(back, tt.msg) { | |||
| 388 | + | t.Fatalf("round trip = %#v, want %#v", back, tt.msg) | |||
| 389 | + | } | |||
| 390 | + | }) | |||
| 391 | + | } | |||
| 392 | + | ||||
| 393 | + | full := MarshalSessions(tests[1].msg) | |||
| 394 | + | assertAllPrefixesFail(t, full, UnmarshalSessions) | |||
| 395 | + | assertTrailingGarbageFails(t, full, UnmarshalSessions) | |||
| 396 | + | } | |||
| 397 | + | ||||
| 302 | // TestMsgTypeConstantsDistinct guards the enumerations defined in this | 398 | // TestMsgTypeConstantsDistinct guards the enumerations defined in this | ||
| 303 | // package against accidental collisions (Intent, ErrorMsg code, Detached | 399 | // package against accidental collisions (Intent, ErrorMsg code, Detached | ||
| 304 | // reason are each independent small spaces, but duplicates within one | 400 | // reason are each independent small spaces, but duplicates within one | ||
M internal/ports/frame.go +3 -0
| @@ -14,12 +14,15 @@ const ( | |||
| 14 | 14 | MsgResize MsgType = 3 | |
| 15 | 15 | MsgDetach MsgType = 4 | |
| 16 | 16 | MsgPing MsgType = 5 | |
| 17 | + | MsgList MsgType = 6 | |
| 18 | + | MsgKill MsgType = 7 | |
| 17 | 19 | ||
| 18 | 20 | MsgWelcome MsgType = 16 | |
| 19 | 21 | MsgError MsgType = 17 | |
| 20 | 22 | MsgOutput MsgType = 18 | |
| 21 | 23 | MsgDetached MsgType = 19 | |
| 22 | 24 | MsgPong MsgType = 20 | |
| 25 | + | MsgSessions MsgType = 21 | |
| 23 | 26 | ) | |
| 24 | 27 | ||
| 25 | 28 | // Frame is the unit of exchange over a Transport: a typed, length-delimited | |
| @@ -14,12 +14,15 @@ const ( | |||||
| 14 | MsgResize MsgType = 3 | 14 | MsgResize MsgType = 3 | ||
| 15 | MsgDetach MsgType = 4 | 15 | MsgDetach MsgType = 4 | ||
| 16 | MsgPing MsgType = 5 | 16 | MsgPing MsgType = 5 | ||
| 17 | + | MsgList MsgType = 6 | |||
| 18 | + | MsgKill MsgType = 7 | |||
| 17 | 19 | ||||
| 18 | MsgWelcome MsgType = 16 | 20 | MsgWelcome MsgType = 16 | ||
| 19 | MsgError MsgType = 17 | 21 | MsgError MsgType = 17 | ||
| 20 | MsgOutput MsgType = 18 | 22 | MsgOutput MsgType = 18 | ||
| 21 | MsgDetached MsgType = 19 | 23 | MsgDetached MsgType = 19 | ||
| 22 | MsgPong MsgType = 20 | 24 | MsgPong MsgType = 20 | ||
| 25 | + | MsgSessions MsgType = 21 | |||
| 23 | ) | 26 | ) | ||
| 24 | 27 | ||||
| 25 | // Frame is the unit of exchange over a Transport: a typed, length-delimited | 28 | // Frame is the unit of exchange over a Transport: a typed, length-delimited | ||
M internal/ports/frame_test.go +3 -0
| @@ -12,11 +12,14 @@ func TestMsgTypeUnique(t *testing.T) { | |||
| 12 | 12 | {"MsgResize", MsgResize}, | |
| 13 | 13 | {"MsgDetach", MsgDetach}, | |
| 14 | 14 | {"MsgPing", MsgPing}, | |
| 15 | + | {"MsgList", MsgList}, | |
| 16 | + | {"MsgKill", MsgKill}, | |
| 15 | 17 | {"MsgWelcome", MsgWelcome}, | |
| 16 | 18 | {"MsgError", MsgError}, | |
| 17 | 19 | {"MsgOutput", MsgOutput}, | |
| 18 | 20 | {"MsgDetached", MsgDetached}, | |
| 19 | 21 | {"MsgPong", MsgPong}, | |
| 22 | + | {"MsgSessions", MsgSessions}, | |
| 20 | 23 | } | |
| 21 | 24 | ||
| 22 | 25 | seen := make(map[MsgType]string, len(tests)) | |
| @@ -12,11 +12,14 @@ func TestMsgTypeUnique(t *testing.T) { | |||||
| 12 | {"MsgResize", MsgResize}, | 12 | {"MsgResize", MsgResize}, | ||
| 13 | {"MsgDetach", MsgDetach}, | 13 | {"MsgDetach", MsgDetach}, | ||
| 14 | {"MsgPing", MsgPing}, | 14 | {"MsgPing", MsgPing}, | ||
| 15 | + | {"MsgList", MsgList}, | |||
| 16 | + | {"MsgKill", MsgKill}, | |||
| 15 | {"MsgWelcome", MsgWelcome}, | 17 | {"MsgWelcome", MsgWelcome}, | ||
| 16 | {"MsgError", MsgError}, | 18 | {"MsgError", MsgError}, | ||
| 17 | {"MsgOutput", MsgOutput}, | 19 | {"MsgOutput", MsgOutput}, | ||
| 18 | {"MsgDetached", MsgDetached}, | 20 | {"MsgDetached", MsgDetached}, | ||
| 19 | {"MsgPong", MsgPong}, | 21 | {"MsgPong", MsgPong}, | ||
| 22 | + | {"MsgSessions", MsgSessions}, | |||
| 20 | } | 23 | } | ||
| 21 | 24 | ||||
| 22 | seen := make(map[MsgType]string, len(tests)) | 25 | seen := make(map[MsgType]string, len(tests)) | ||