package main

import (
	"testing"
	"time"
)

// The relay's plane to this bridge is loopback, so its own buffered-amount
// signal never reports congestion. These reports are the only send-side
// truth it gets: they must fire on real overload and stay quiet otherwise,
// or the governor either never adapts or throttles a healthy path.
func TestFramePressureReport(t *testing.T) {
	for _, testCase := range []struct {
		name            string
		dropped, opened uint64
		blocked         time.Duration
		queue           int
		want            string
	}{{
		name: "quiet interval reports nothing",
	}, {
		// Latest-wins dropping is how this writer absorbs jitter: the relay
		// paces a little above the path on purpose. A healthy session that
		// drops a few frames must not be cut.
		name:    "a small dropped share is normal jitter absorption",
		dropped: 4,
		opened:  30,
		queue:   1,
	}, {
		// The ratchet this guards: at a cadence already cut to ~5 fps, four
		// dropped frames are "44% of offered" and would cut it again.
		name:    "a few drops at low cadence are not overload",
		dropped: 4,
		opened:  5,
		queue:   1,
	}, {
		name:    "a settled stream blocked for its own transmission time",
		opened:  4,
		blocked: 480 * time.Millisecond,
	}, {
		name:    "dropping more than a third means the cadence does not fit",
		dropped: 15,
		opened:  5,
		blocked: 300 * time.Millisecond,
		queue:   6,
		want: `{"t":"framePressure","dropped":15,"offered":20,` +
			`"blockedMs":300,"queue":6}`,
	}, {
		name:    "a wedged write with frames waiting behind it reports too",
		dropped: 7,
		opened:  3,
		blocked: 515 * time.Millisecond,
		queue:   7,
		want: `{"t":"framePressure","dropped":7,"offered":10,` +
			`"blockedMs":515,"queue":7}`,
	}, {
		// A settled stream routinely blocks for a frame's transmission time
		// with a fragment or two queued behind it; three dropped frames is
		// not a wedge, and cutting for it walked the engine down to 6 fps.
		name:    "a blocked write with only a few drops is not a wedge",
		dropped: 3,
		opened:  9,
		blocked: 300 * time.Millisecond,
		queue:   1,
	}} {
		t.Run(testCase.name, func(t *testing.T) {
			report, ok := framePressureReport(testCase.dropped,
				testCase.opened, testCase.blocked, testCase.queue)
			if ok != (testCase.want != "") {
				t.Fatalf("reported=%v, want %v", ok, testCase.want != "")
			}
			if report != testCase.want {
				t.Fatalf("report=%q, want %q", report, testCase.want)
			}
		})
	}
}

// A link hold drops one second's worth of frames and then flushes; only a
// cadence that does not fit drops every second.
func TestPressureGateNeedsConsecutiveSamples(t *testing.T) {
	var gate pressureGate
	overload := func() bool {
		_, ok := gate.report(15, 5, 300*time.Millisecond, 6)
		return ok
	}
	quiet := func() bool {
		_, ok := gate.report(0, 13, 40*time.Millisecond, 0)
		return ok
	}
	if overload() {
		t.Fatal("one sample of drops (a hold) must not report")
	}
	if quiet() {
		t.Fatal("a quiet sample must not report")
	}
	if overload() {
		t.Fatal("a quiet sample must reset the streak")
	}
	if !overload() {
		t.Fatal("two consecutive overloaded samples must report")
	}
	if !overload() {
		t.Fatal("a standing overload keeps reporting every sample")
	}
}

// The relay parses a leading digit as a byte-count ack and '{' as a report;
// an ack that could be mistaken for a report would corrupt its in-flight
// accounting.
func TestFramePressureReportIsDistinguishableFromAnAck(t *testing.T) {
	report, ok := framePressureReport(framePressureMinDrops, 1, framePressureBlock, 1)
	if !ok {
		t.Fatal("expected a report")
	}
	if report[0] != '{' {
		t.Fatalf("report starts with %q, want '{'", report[0])
	}
}

// The measured shape of the failure this whole path exists to catch: the
// relay paced 20 fps into a link carrying 5, and the writer discarded most
// of what it was handed while the relay saw an empty loopback socket.
func TestFramePressureReportCatchesTheMeasuredOverload(t *testing.T) {
	if _, ok := framePressureReport(15, 5, 516*time.Millisecond, 8); !ok {
		t.Fatal("the 2026-08-22 overload must report pressure")
	}
	// ... and stays quiet once the governor has cut to a cadence that fits.
	if _, ok := framePressureReport(0, 4, 348*time.Millisecond, 0); ok {
		t.Fatal("a settled stream must not report pressure")
	}
}
