package notify import ( "testing" "time" ) func TestClientTransferMonitorTracksSendLifecycle(t *testing.T) { client := NewClient().(*ClientCommon) monitor := client.getFileTransferState().monitorView() now := time.Unix(100, 0) client.publishSendFileEvent(FileEvent{ NetType: NET_CLIENT, Kind: EnvelopeFileMeta, Packet: FilePacket{FileID: "send-1", Size: 100}, Path: "/tmp/send-1.bin", Total: 100, Time: now, }) client.publishSendFileEvent(FileEvent{ NetType: NET_CLIENT, Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "send-1", Size: 100}, Path: "/tmp/send-1.bin", Received: 40, Total: 100, Percent: 40, StartedAt: now, UpdatedAt: now.Add(2 * time.Second), Duration: 2 * time.Second, RateBPS: 20, Time: now.Add(2 * time.Second), StepDuration: 2 * time.Second, }) active := monitor.activeSnapshots() if got, want := len(active), 1; got != want { t.Fatalf("active count mismatch: got %d want %d", got, want) } if got, want := active[0].Direction, fileTransferDirectionSend; got != want { t.Fatalf("direction mismatch: got %v want %v", got, want) } if got, want := active[0].Scope, clientFileScope(); got != want { t.Fatalf("scope mismatch: got %q want %q", got, want) } if got, want := active[0].Received, int64(40); got != want { t.Fatalf("received mismatch: got %d want %d", got, want) } snapshot, ok := monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "send-1") if !ok { t.Fatal("latest snapshot should exist while active") } if got, want := snapshot.Kind, EnvelopeFileChunk; got != want { t.Fatalf("latest active kind mismatch: got %v want %v", got, want) } if got, want := snapshot.Received, int64(40); got != want { t.Fatalf("latest active received mismatch: got %d want %d", got, want) } client.publishSendFileEvent(FileEvent{ NetType: NET_CLIENT, Kind: EnvelopeFileEnd, Packet: FilePacket{FileID: "send-1", Size: 100}, Path: "/tmp/send-1.bin", Received: 100, Total: 100, Percent: 100, Done: true, StartedAt: now, UpdatedAt: now.Add(4 * time.Second), Duration: 4 * time.Second, RateBPS: 25, Time: now.Add(4 * time.Second), }) active = monitor.activeSnapshots() if got, want := len(active), 0; got != want { t.Fatalf("active count after end mismatch: got %d want %d", got, want) } completed := monitor.completedSnapshots() if got, want := len(completed), 1; got != want { t.Fatalf("completed count mismatch: got %d want %d", got, want) } if got, want := completed[0].Done, true; got != want { t.Fatalf("done mismatch: got %v want %v", got, want) } if got, want := completed[0].Received, int64(100); got != want { t.Fatalf("completed received mismatch: got %d want %d", got, want) } snapshot, ok = monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "send-1") if !ok { t.Fatal("latest snapshot should exist after completion") } if got, want := snapshot.Kind, EnvelopeFileEnd; got != want { t.Fatalf("latest completed kind mismatch: got %v want %v", got, want) } if got, want := snapshot.Done, true; got != want { t.Fatalf("latest completed done mismatch: got %v want %v", got, want) } } func TestServerTransferMonitorUsesClientScope(t *testing.T) { server := NewServer().(*ServerCommon) monitor := server.getFileTransferState().monitorView() client := &ClientConn{ClientID: "client-1"} now := time.Unix(200, 0) server.publishReceivedFileEvent(FileEvent{ NetType: NET_SERVER, ClientConn: client, Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "recv-1", Size: 50}, Path: "/tmp/recv-1.part", Received: 20, Total: 50, Percent: 40, StartedAt: now, UpdatedAt: now.Add(time.Second), Duration: time.Second, RateBPS: 20, Time: now.Add(time.Second), }) active := monitor.activeSnapshots() if got, want := len(active), 1; got != want { t.Fatalf("active count mismatch: got %d want %d", got, want) } if got, want := active[0].Direction, fileTransferDirectionReceive; got != want { t.Fatalf("direction mismatch: got %v want %v", got, want) } if got, want := active[0].Scope, serverFileScope(client); got != want { t.Fatalf("scope mismatch: got %q want %q", got, want) } if got, want := active[0].FileID, "recv-1"; got != want { t.Fatalf("fileID mismatch: got %q want %q", got, want) } } func TestTransferMonitorDirectionQueries(t *testing.T) { monitor := newFileTransferMonitor() now := time.Unix(300, 0) monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "shared", Size: 10}, Received: 4, Total: 10, Time: now, }) monitor.observe(fileTransferDirectionReceive, FileEvent{ Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "shared", Size: 10}, Received: 7, Total: 10, Time: now.Add(time.Second), }) sendSnapshots := monitor.activeSnapshotsByDirection(fileTransferDirectionSend) if got, want := len(sendSnapshots), 1; got != want { t.Fatalf("send snapshots count mismatch: got %d want %d", got, want) } if got, want := sendSnapshots[0].Received, int64(4); got != want { t.Fatalf("send snapshot received mismatch: got %d want %d", got, want) } recvSnapshots := monitor.activeSnapshotsByDirection(fileTransferDirectionReceive) if got, want := len(recvSnapshots), 1; got != want { t.Fatalf("recv snapshots count mismatch: got %d want %d", got, want) } if got, want := recvSnapshots[0].Received, int64(7); got != want { t.Fatalf("recv snapshot received mismatch: got %d want %d", got, want) } sendSnapshot, ok := monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "shared") if !ok { t.Fatal("send latest snapshot should exist") } if got, want := sendSnapshot.Received, int64(4); got != want { t.Fatalf("send latest received mismatch: got %d want %d", got, want) } recvSnapshot, ok := monitor.latestSnapshot(fileTransferDirectionReceive, clientFileScope(), "shared") if !ok { t.Fatal("recv latest snapshot should exist") } if got, want := recvSnapshot.Received, int64(7); got != want { t.Fatalf("recv latest received mismatch: got %d want %d", got, want) } } func TestTransferMonitorSnapshotsByFileID(t *testing.T) { monitor := newFileTransferMonitor() now := time.Unix(400, 0) serverClientA := &ClientConn{ClientID: "client-a"} serverClientB := &ClientConn{ClientID: "client-b"} monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "shared", Size: 20}, Received: 8, Total: 20, Time: now, }) monitor.observe(fileTransferDirectionReceive, FileEvent{ ClientConn: serverClientA, Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "shared", Size: 20}, Received: 12, Total: 20, Time: now.Add(time.Second), }) monitor.observe(fileTransferDirectionReceive, FileEvent{ ClientConn: serverClientB, Kind: EnvelopeFileEnd, Packet: FilePacket{FileID: "shared", Size: 20}, Received: 20, Total: 20, Done: true, Time: now.Add(2 * time.Second), }) allSnapshots := monitor.snapshotsByFileID("shared") if got, want := len(allSnapshots), 3; got != want { t.Fatalf("all snapshots count mismatch: got %d want %d", got, want) } if got, want := allSnapshots[0].Direction, fileTransferDirectionReceive; got != want { t.Fatalf("first snapshot direction mismatch: got %v want %v", got, want) } if got, want := allSnapshots[0].Scope, serverFileScope(serverClientA); got != want { t.Fatalf("first snapshot scope mismatch: got %q want %q", got, want) } if got, want := allSnapshots[1].Scope, serverFileScope(serverClientB); got != want { t.Fatalf("second snapshot scope mismatch: got %q want %q", got, want) } if got, want := allSnapshots[2].Direction, fileTransferDirectionSend; got != want { t.Fatalf("third snapshot direction mismatch: got %v want %v", got, want) } recvSnapshots := monitor.snapshotsByDirectionAndFileID(fileTransferDirectionReceive, "shared") if got, want := len(recvSnapshots), 2; got != want { t.Fatalf("recv snapshots count mismatch: got %d want %d", got, want) } if got, want := recvSnapshots[0].Scope, serverFileScope(serverClientA); got != want { t.Fatalf("recv first scope mismatch: got %q want %q", got, want) } if got, want := recvSnapshots[1].Scope, serverFileScope(serverClientB); got != want { t.Fatalf("recv second scope mismatch: got %q want %q", got, want) } if got, want := recvSnapshots[1].Done, true; got != want { t.Fatalf("recv completed snapshot mismatch: got %v want %v", got, want) } sendSnapshots := monitor.snapshotsByDirectionAndFileID(fileTransferDirectionSend, "shared") if got, want := len(sendSnapshots), 1; got != want { t.Fatalf("send snapshots count mismatch: got %d want %d", got, want) } if got, want := sendSnapshots[0].Received, int64(8); got != want { t.Fatalf("send snapshot received mismatch: got %d want %d", got, want) } } func TestTransferMonitorCompletedRetentionEvictsOldest(t *testing.T) { monitor := newFileTransferMonitorWithCompletedLimit(2) now := time.Unix(500, 0) monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileChunk, Packet: FilePacket{FileID: "active-1", Size: 10}, Received: 3, Total: 10, Time: now, }) monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileEnd, Packet: FilePacket{FileID: "done-1", Size: 10}, Received: 10, Total: 10, Done: true, Time: now.Add(time.Second), }) monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileEnd, Packet: FilePacket{FileID: "done-2", Size: 10}, Received: 10, Total: 10, Done: true, Time: now.Add(2 * time.Second), }) monitor.observe(fileTransferDirectionSend, FileEvent{ Kind: EnvelopeFileEnd, Packet: FilePacket{FileID: "done-3", Size: 10}, Received: 10, Total: 10, Done: true, Time: now.Add(3 * time.Second), }) active := monitor.activeSnapshots() if got, want := len(active), 1; got != want { t.Fatalf("active count mismatch: got %d want %d", got, want) } if got, want := active[0].FileID, "active-1"; got != want { t.Fatalf("active fileID mismatch: got %q want %q", got, want) } completed := monitor.completedSnapshots() if got, want := len(completed), 2; got != want { t.Fatalf("completed count mismatch: got %d want %d", got, want) } if got, want := completed[0].FileID, "done-2"; got != want { t.Fatalf("first completed fileID mismatch: got %q want %q", got, want) } if got, want := completed[1].FileID, "done-3"; got != want { t.Fatalf("second completed fileID mismatch: got %q want %q", got, want) } if _, ok := monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "done-1"); ok { t.Fatal("oldest completed snapshot should be evicted") } if _, ok := monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "done-3"); !ok { t.Fatal("latest completed snapshot should be retained") } if snapshot, ok := monitor.latestSnapshot(fileTransferDirectionSend, clientFileScope(), "active-1"); !ok { t.Fatal("active snapshot should remain available") } else if got, want := snapshot.Kind, EnvelopeFileChunk; got != want { t.Fatalf("active latest kind mismatch: got %v want %v", got, want) } }