chore: release v0.25.8
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A node whose whole greeting is a bare "login:" — no newline, no banner — and
|
||||
// which then demands a password must still log in.
|
||||
//
|
||||
// Reported on f5mzn.org:9000: telnet by hand worked, OpsLog sat at "connecting"
|
||||
// for ever. Two faults met there. The prompts carry no newline, and the read
|
||||
// loop only ever looked at complete LINES, so neither prompt was seen at all;
|
||||
// and the blind 1.5 s login never recorded that it had sent the callsign, which
|
||||
// left the password branch — gated on that flag — permanently switched off.
|
||||
//
|
||||
// The test speaks the node's side literally: "login:" with no newline, then
|
||||
// "password:" with no newline, then a spot. It asserts both answers arrive and
|
||||
// that the session reaches Connected without any welcome banner to lean on.
|
||||
func TestBareLoginAndPasswordPromptsWithoutNewlines(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
host, portStr, _ := net.SplitHostPort(ln.Addr().String())
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type answers struct{ login, pwd string }
|
||||
got := make(chan answers, 1)
|
||||
go func() {
|
||||
c, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
rd := bufio.NewReader(c)
|
||||
// No newline, exactly as the node sends it.
|
||||
if _, err := c.Write([]byte("login: ")); err != nil {
|
||||
return
|
||||
}
|
||||
call, err := rd.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := c.Write([]byte("password: ")); err != nil {
|
||||
return
|
||||
}
|
||||
pwd, err := rd.ReadString('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
got <- answers{strings.TrimSpace(call), strings.TrimSpace(pwd)}
|
||||
// Something to prove the link is live and parsing again afterwards.
|
||||
_, _ = c.Write([]byte("DX de F4BPO: 14074.0 OY1CT FT8 1234Z\r\n"))
|
||||
time.Sleep(time.Second)
|
||||
}()
|
||||
|
||||
spots := make(chan Spot, 4)
|
||||
s := &session{
|
||||
cfg: ServerConfig{Name: "pwd node", Host: host, Port: port, Password: "s3cret"},
|
||||
login: "F4BPO",
|
||||
onSpot: func(sp Spot) { spots <- sp },
|
||||
onLine: func(Line) {},
|
||||
onStatus: func() {},
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
done := make(chan error, 1)
|
||||
go func() { _, err := s.runOnce(); done <- err }()
|
||||
defer func() { close(s.stopCh); <-done }()
|
||||
|
||||
select {
|
||||
case a := <-got:
|
||||
if a.login != "F4BPO" {
|
||||
t.Errorf("callsign sent = %q, want F4BPO", a.login)
|
||||
}
|
||||
if a.pwd != "s3cret" {
|
||||
t.Errorf("password sent = %q, want s3cret — the prompt carried no newline", a.pwd)
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("the node's newline-less prompts were never answered")
|
||||
}
|
||||
|
||||
select {
|
||||
case sp := <-spots:
|
||||
if sp.DXCall != "OY1CT" {
|
||||
t.Errorf("spot from the wrong station: %+v", sp)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("no spot after the login — the stream is not being parsed")
|
||||
}
|
||||
|
||||
// Connected without a welcome banner: the handshake alone must be enough.
|
||||
if st := s.snapshot().State; st != StateConnected {
|
||||
t.Errorf("state = %q, want %q — the server would still show as connecting", st, StateConnected)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user