package main

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"slices"
	"strings"
	"testing"
)

func TestReadBounded(t *testing.T) {
	for _, tc := range []struct {
		input    string
		max      int64
		tooLarge bool
	}{{"", 0, false}, {"four", 4, false}, {"five!", 4, true}} {
		got, err := ReadBounded(strings.NewReader(tc.input), tc.max)
		if tc.tooLarge {
			if !errors.Is(err, ErrTooLarge) {
				t.Fatal(err)
			}
		} else if err != nil || string(got) != tc.input {
			t.Fatal(string(got), err)
		}
	}
}
func TestScannerLimits(t *testing.T) {
	input := "a\n" + strings.Repeat("x", 70_000) + "\nz\n"
	if got, err := scanLengths(input, 0); err == nil || !slices.Equal(got, []int{1}) {
		t.Fatal(got, err)
	}
	if got, err := scanLengths(input, 1<<20); err != nil || !slices.Equal(got, []int{1, 70_000, 1}) {
		t.Fatal(got, err)
	}
}
func TestSensitiveLog(t *testing.T) {
	var b bytes.Buffer
	newLogger(&b).Info("test", "token", Secret("sample-secret"))
	if strings.Contains(b.String(), "sample-secret") || !strings.Contains(b.String(), "[REDACTED]") {
		t.Fatal(b.String())
	}
}

type brokenWriter struct{}

func (brokenWriter) Write(p []byte) (int, error) { return len(p) - 1, nil }

type readerOnly struct{ io.Reader }

func TestCopyDetectsShortWrite(t *testing.T) {
	_, err := io.Copy(brokenWriter{}, readerOnly{strings.NewReader("data")})
	if !errors.Is(err, io.ErrShortWrite) {
		t.Fatal(err)
	}
}
func TestMaxBytesReader(t *testing.T) {
	request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("12345"))
	recorder := httptest.NewRecorder()
	limited := http.MaxBytesReader(recorder, request.Body, 4)
	defer limited.Close()
	_, err := io.ReadAll(limited)
	var tooLarge *http.MaxBytesError
	if !errors.As(err, &tooLarge) || tooLarge.Limit != 4 {
		t.Fatal(err)
	}
}

func TestDecodeOne(t *testing.T) {
	for _, tc := range []struct {
		input   string
		wantErr bool
	}{
		{`{"name":"Ada"} `, false},
		{`{"name":"Ada"} {"name":"Lin"}`, true},
		{`{"unknown":1}`, true},
		{`{"name":"Ada"} broken`, true},
	} {
		var user User
		err := DecodeOne(strings.NewReader(tc.input), &user)
		if (err != nil) != tc.wantErr {
			t.Fatalf("%q: %v", tc.input, err)
		}
	}
}
