mirror of
https://github.com/renorris/openfsd
synced 2026-03-22 22:57:05 +08:00
Changes:
- Implement bootstrapping library for managing several concurrent internal services
- Refactor concurrency model for connections/logical clients and their associated I/O
- Refactor server context singleton
- Refactor error handling
- Most errors are now gracefully sent to the FSD client directly encoded as an $ER packet,
enhancing visibility and debugging
- Most errors are now rightfully treated as non-fatal
- Refactor package/dependency graph
- Refactor calling conventions/interfaces for many packages
- Refactor database package
- Refactor post office
Features:
- Add VATSIM-esque HTTP/JSON "data feed"
- Add ephemeral in-memory database option
- Add user management REST API
- Add improved web interface
- Add MySQL support (drop SQLite support)
1073 lines
29 KiB
Go
1073 lines
29 KiB
Go
package test
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/renorris/openfsd/auth"
|
|
"github.com/renorris/openfsd/bootstrap"
|
|
"github.com/renorris/openfsd/database"
|
|
"github.com/renorris/openfsd/protocol"
|
|
"github.com/renorris/openfsd/servercontext"
|
|
"github.com/stretchr/testify/assert"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type clientStruct struct {
|
|
callsign string
|
|
cid int
|
|
password string
|
|
clientID uint16
|
|
clientName string
|
|
majorVersion int
|
|
minorVersion int
|
|
sysUID int
|
|
initialChallenge string
|
|
networkRating int
|
|
protocolRevsion int
|
|
simulatorType int
|
|
realName string
|
|
|
|
preliminaryTestPackets []protocol.PDU // Packets to send after logging in, but before the next browser logs in
|
|
preliminaryWantPackets []protocol.PDU // Expected packets to receive before the next browser logs in
|
|
testPackets []protocol.PDU // Packets to send in normal post-login state
|
|
wantPackets []protocol.PDU // Expected packets to receive
|
|
}
|
|
|
|
// TestFSDClientLogic focuses on post-login logic
|
|
func TestFSDClientLogic(t *testing.T) {
|
|
if err := os.Setenv("IN_MEMORY_DB", "true"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Start the server
|
|
ctx, cancelCtx := context.WithCancel(context.Background())
|
|
b := bootstrap.NewDefaultBootstrap()
|
|
if err := b.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Add test user
|
|
user1 := database.FSDUserRecord{
|
|
Email: "example@mail.com",
|
|
FirstName: "Test User 1",
|
|
LastName: "Test User 1 Lastname",
|
|
Password: "54321",
|
|
FSDPassword: "12345",
|
|
NetworkRating: protocol.NetworkRatingOBS,
|
|
PilotRating: 0,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
var err error
|
|
user1.CID, err = user1.Insert(servercontext.DB())
|
|
assert.Nil(t, err)
|
|
|
|
user2 := database.FSDUserRecord{
|
|
Email: "example@mail.com",
|
|
FirstName: "Test User 2",
|
|
LastName: "Test User 2 Lastname",
|
|
Password: "54321",
|
|
FSDPassword: "12345",
|
|
NetworkRating: protocol.NetworkRatingOBS,
|
|
PilotRating: 0,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
user2.CID, err = user2.Insert(servercontext.DB())
|
|
assert.Nil(t, err)
|
|
|
|
user3 := database.FSDUserRecord{
|
|
Email: "example@mail.com",
|
|
FirstName: "Test User 3",
|
|
LastName: "Test User 3 Lastname",
|
|
Password: "54321",
|
|
FSDPassword: "12345",
|
|
NetworkRating: protocol.NetworkRatingSUP,
|
|
PilotRating: 0,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
user3.CID, err = user3.Insert(servercontext.DB())
|
|
assert.Nil(t, err)
|
|
|
|
tests := []struct {
|
|
testName string
|
|
clients []clientStruct
|
|
}{
|
|
{
|
|
testName: "Ping ($PI) request",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.PingPDU{
|
|
From: "N123",
|
|
To: protocol.ServerCallsign,
|
|
Timestamp: "1234567890",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.PongPDU{
|
|
From: protocol.ServerCallsign,
|
|
To: "N123",
|
|
Timestamp: "1234567890",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "IP request ($CQCLIENT:SERVER:IP)",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.ClientQueryPDU{
|
|
From: "N123",
|
|
To: protocol.ServerCallsign,
|
|
QueryType: protocol.ClientQueryPublicIP,
|
|
Payload: "",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.ClientQueryResponsePDU{
|
|
From: protocol.ServerCallsign,
|
|
To: "N123",
|
|
QueryType: protocol.ClientQueryPublicIP,
|
|
Payload: "127.0.0.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Fast pilot position broadcast",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N123",
|
|
SquawkCode: "1200",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
&protocol.PingPDU{
|
|
From: "N123",
|
|
To: protocol.ServerCallsign,
|
|
Timestamp: "1234567890",
|
|
},
|
|
},
|
|
preliminaryWantPackets: []protocol.PDU{
|
|
&protocol.PongPDU{
|
|
From: protocol.ServerCallsign,
|
|
To: "N123",
|
|
Timestamp: "1234567890",
|
|
},
|
|
},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: 1,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1201",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
&protocol.FastPilotPositionPDU{
|
|
Type: protocol.FastPilotPositionTypeFast,
|
|
From: "N124",
|
|
Lat: 45,
|
|
Lng: 45,
|
|
AltitudeTrue: 10,
|
|
AltitudeAgl: 10,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
PositionalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
RotationalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
NoseGearAngle: 15,
|
|
},
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1201",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1201",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
&protocol.FastPilotPositionPDU{
|
|
Type: protocol.FastPilotPositionTypeFast,
|
|
From: "N124",
|
|
Lat: 45,
|
|
Lng: 45,
|
|
AltitudeTrue: 10,
|
|
AltitudeAgl: 10,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
PositionalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
RotationalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
NoseGearAngle: 15,
|
|
},
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1201",
|
|
NetworkRating: 1,
|
|
Lat: -45.0,
|
|
Lng: -45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
&protocol.FastPilotPositionPDU{
|
|
Type: protocol.FastPilotPositionTypeFast,
|
|
From: "N124",
|
|
Lat: -45,
|
|
Lng: -45,
|
|
AltitudeTrue: 10,
|
|
AltitudeAgl: 10,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
PositionalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
RotationalVelocityVector: protocol.VelocityVector{
|
|
X: 10,
|
|
Y: 10,
|
|
Z: 10,
|
|
},
|
|
NoseGearAngle: 15,
|
|
},
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1201",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Pilot position broadcast",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N123",
|
|
SquawkCode: "1200",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: 1,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1200",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.PilotPositionPDU{
|
|
SquawkingModeC: false,
|
|
Identing: false,
|
|
From: "N124",
|
|
SquawkCode: "1200",
|
|
NetworkRating: 1,
|
|
Lat: 45.0,
|
|
Lng: 45.0,
|
|
TrueAltitude: 50,
|
|
PressureAltitude: 50,
|
|
GroundSpeed: 0,
|
|
Pitch: 0,
|
|
Heading: 0,
|
|
Bank: 0,
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Client Query Real Name ($CQN123:N124:RN)",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: 1,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.ClientQueryPDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
QueryType: protocol.ClientQueryRealName,
|
|
Payload: "",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.ClientQueryPDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
QueryType: protocol.ClientQueryRealName,
|
|
Payload: "",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Client Query Real Name Response ($CRN124:N123:RN:Foo Bar)",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: 1,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.ClientQueryResponsePDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
QueryType: protocol.ClientQueryRealName,
|
|
Payload: "Foo Bar",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.ClientQueryResponsePDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
QueryType: protocol.ClientQueryRealName,
|
|
Payload: "Foo Bar",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Authentication challenge ($ZC)",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "30984979d8caed23",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.AuthChallengePDU{
|
|
From: "N123",
|
|
To: protocol.ServerCallsign,
|
|
Challenge: "de6acb8e",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AuthChallengeResponsePDU{
|
|
From: protocol.ServerCallsign,
|
|
To: "N123",
|
|
ChallengeResponse: "f8ee97157f66455ed6108fccef6ccf5f",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "kill ($!!)",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "SUP",
|
|
To: protocol.ServerCallsign,
|
|
CID: user3.CID,
|
|
Token: "",
|
|
NetworkRating: protocol.NetworkRatingSUP,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Supervisor",
|
|
},
|
|
&protocol.KillRequestPDU{
|
|
From: "SUP",
|
|
To: "N123",
|
|
Reason: "ur banned",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "SUP",
|
|
cid: user3.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: protocol.NetworkRatingSUP,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Supervisor",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.KillRequestPDU{
|
|
From: "SUP",
|
|
To: "N123",
|
|
Reason: "ur banned",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "kill not allowed",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "N123",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: protocol.NetworkRatingOBS,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.TextMessagePDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
Message: "hello",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: protocol.NetworkRatingOBS,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.KillRequestPDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
Reason: "ur banned",
|
|
},
|
|
&protocol.TextMessagePDU{
|
|
From: "N124",
|
|
To: "N123",
|
|
Message: "hello",
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
testName: "Delete pilot broadcast",
|
|
clients: []clientStruct{
|
|
{
|
|
callsign: "DEL",
|
|
cid: user1.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: 1,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "John Doe",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{},
|
|
wantPackets: []protocol.PDU{
|
|
&protocol.AddPilotPDU{
|
|
From: "N124",
|
|
To: protocol.ServerCallsign,
|
|
CID: user2.CID,
|
|
Token: "",
|
|
NetworkRating: protocol.NetworkRatingOBS,
|
|
ProtocolRevision: protocol.ProtoRevisionVatsim2022,
|
|
SimulatorType: 2,
|
|
RealName: "Foo Bar",
|
|
},
|
|
&protocol.DeletePilotPDU{
|
|
From: "N124",
|
|
CID: user2.CID,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
callsign: "N124",
|
|
cid: user2.CID,
|
|
password: "12345",
|
|
clientID: 35044,
|
|
clientName: "vPilot",
|
|
majorVersion: 3,
|
|
minorVersion: 8,
|
|
sysUID: -99999,
|
|
initialChallenge: "abcdef",
|
|
networkRating: protocol.NetworkRatingOBS,
|
|
protocolRevsion: protocol.ProtoRevisionVatsim2022,
|
|
simulatorType: 2,
|
|
realName: "Foo Bar",
|
|
|
|
preliminaryTestPackets: []protocol.PDU{},
|
|
preliminaryWantPackets: []protocol.PDU{},
|
|
testPackets: []protocol.PDU{
|
|
&protocol.DeletePilotPDU{
|
|
From: "N124",
|
|
CID: user2.CID,
|
|
},
|
|
},
|
|
wantPackets: []protocol.PDU{},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Run tests
|
|
for _, tc := range tests {
|
|
t.Run(tc.testName, func(t *testing.T) {
|
|
var doneWg sync.WaitGroup
|
|
// Spawn each browser
|
|
for _, client := range tc.clients {
|
|
c := client
|
|
doneWg.Add(1)
|
|
|
|
var loggedIn sync.WaitGroup
|
|
loggedIn.Add(1)
|
|
|
|
go func() {
|
|
defer doneWg.Done()
|
|
|
|
// Log in the browser.
|
|
// Test cases are meant to be executed after the browser has logged in.
|
|
// Load a JWT token first
|
|
var token string
|
|
var err error
|
|
token, err = getJWTToken(c.cid, c.password)
|
|
assert.Nil(t, err)
|
|
|
|
conn, err := net.Dial("tcp4", servercontext.Config().FSDListenAddress)
|
|
assert.Nil(t, err)
|
|
|
|
err = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
|
assert.Nil(t, err)
|
|
defer func() {
|
|
closeErr := conn.Close()
|
|
assert.Nil(t, closeErr)
|
|
}()
|
|
|
|
reader := bufio.NewReader(conn)
|
|
serverIdent, err := reader.ReadString('\n')
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, serverIdent)
|
|
assert.True(t, strings.HasPrefix(serverIdent, "$DISERVER:CLIENT:"))
|
|
|
|
clientIdentPDU := protocol.ClientIdentificationPDU{
|
|
From: c.callsign,
|
|
To: protocol.ServerCallsign,
|
|
ClientID: c.clientID,
|
|
ClientName: c.clientName,
|
|
MajorVersion: c.majorVersion,
|
|
MinorVersion: c.minorVersion,
|
|
CID: c.cid,
|
|
SysUID: c.sysUID,
|
|
InitialChallenge: c.initialChallenge,
|
|
}
|
|
|
|
addPilotPDU := protocol.AddPilotPDU{
|
|
From: c.callsign,
|
|
To: protocol.ServerCallsign,
|
|
CID: c.cid,
|
|
Token: token,
|
|
NetworkRating: protocol.NetworkRating(c.networkRating),
|
|
ProtocolRevision: c.protocolRevsion,
|
|
SimulatorType: c.simulatorType,
|
|
RealName: c.realName,
|
|
}
|
|
|
|
_, err = conn.Write([]byte(clientIdentPDU.Serialize()))
|
|
assert.Nil(t, err)
|
|
_, err = conn.Write([]byte(addPilotPDU.Serialize()))
|
|
assert.Nil(t, err)
|
|
|
|
motdMsg, err := reader.ReadString('\n')
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, motdMsg)
|
|
|
|
motdReceivedPDU := protocol.TextMessagePDU{}
|
|
err = motdReceivedPDU.Parse(motdMsg)
|
|
assert.Nil(t, err)
|
|
|
|
expectedMOTD := protocol.TextMessagePDU{
|
|
From: protocol.ServerCallsign,
|
|
To: c.callsign,
|
|
Message: servercontext.Config().MOTD,
|
|
}
|
|
|
|
assert.Equal(t, expectedMOTD.Serialize(), motdReceivedPDU.Serialize())
|
|
|
|
// Send post-login packets before signalling that we're done logging in
|
|
for _, packet := range c.preliminaryTestPackets {
|
|
_, writeErr := conn.Write([]byte(packet.Serialize()))
|
|
assert.Nil(t, writeErr)
|
|
}
|
|
|
|
// Verify post-login returned packets are correct
|
|
for _, packet := range c.preliminaryWantPackets {
|
|
deadlineErr := conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
|
assert.Nil(t, deadlineErr)
|
|
|
|
recvPacket, recvErr := reader.ReadString('\n')
|
|
assert.Nil(t, recvErr)
|
|
|
|
assert.Equal(t, packet.Serialize(), recvPacket)
|
|
}
|
|
|
|
// Signal we're done logging in
|
|
loggedIn.Done()
|
|
|
|
// Send test packets
|
|
for _, packet := range c.testPackets {
|
|
_, writeErr := conn.Write([]byte(packet.Serialize()))
|
|
assert.Nil(t, writeErr)
|
|
}
|
|
|
|
// Verify returned packets are correct
|
|
for _, packet := range c.wantPackets {
|
|
deadlineErr := conn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
|
assert.Nil(t, deadlineErr)
|
|
|
|
recvPacket, recvErr := reader.ReadString('\n')
|
|
assert.Nil(t, recvErr)
|
|
|
|
assert.Equal(t, packet.Serialize(), recvPacket)
|
|
}
|
|
}()
|
|
|
|
// Wait for the preceding client to finish logging in before spawning another
|
|
loggedIn.Wait()
|
|
}
|
|
|
|
// Wait for all goroutines to return
|
|
doneWg.Wait()
|
|
})
|
|
}
|
|
|
|
// Close context
|
|
cancelCtx()
|
|
|
|
// Wait for bootstrap done
|
|
<-b.Done
|
|
}
|
|
|
|
func getJWTToken(cid int, password string) (token string, err error) {
|
|
reqPayload := auth.FSDJWTRequest{
|
|
CID: strconv.Itoa(cid),
|
|
Password: password,
|
|
}
|
|
|
|
var reqPayloadBytes []byte
|
|
if reqPayloadBytes, err = json.Marshal(reqPayload); err != nil {
|
|
return
|
|
}
|
|
|
|
var addr *net.TCPAddr
|
|
if addr, err = net.ResolveTCPAddr("tcp4", servercontext.Config().HTTPListenAddress); err != nil {
|
|
return
|
|
}
|
|
|
|
var resp *http.Response
|
|
if resp, err = http.Post("http://localhost:"+strconv.Itoa(addr.Port)+"/api/v1/fsd-jwt", "application/json", bytes.NewReader(reqPayloadBytes)); err != nil {
|
|
return
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
err = errors.New("HTTP status " + resp.Status)
|
|
return
|
|
}
|
|
|
|
var respBody []byte
|
|
if respBody, err = io.ReadAll(resp.Body); err != nil {
|
|
return
|
|
}
|
|
|
|
var respPayload auth.FSDJWTResponse
|
|
if err = json.Unmarshal(respBody, &respPayload); err != nil {
|
|
return
|
|
}
|
|
|
|
if !respPayload.Success {
|
|
err = errors.New("response payload unsuccessful " + respPayload.ErrorMsg)
|
|
return
|
|
}
|
|
|
|
return respPayload.Token, nil
|
|
}
|