package main

import (
	"context"
	"errors"
	"golang.org/x/sync/errgroup"
	"golang.org/x/sync/singleflight"
	"testing"
)

func TestGroupFailure(t *testing.T) {
	if err := GroupFailure(); err == nil || err.Error() != "upstream failed" {
		t.Fatal(err)
	}
}
func TestSingleflight(t *testing.T) {
	if calls, shared := SharedLoad(); calls != 1 || !shared {
		t.Fatal(calls, shared)
	}
}
func TestSemaphore(t *testing.T) {
	peak, err := WeightedWork(context.Background(), 3, []int64{2, 1, 2, 1})
	if err != nil || peak < 1 || peak > 3 {
		t.Fatal(peak, err)
	}
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	if _, err := WeightedWork(ctx, 3, []int64{1}); !errors.Is(err, context.Canceled) {
		t.Fatal(err)
	}
	if _, err := WeightedWork(context.Background(), 3, []int64{4}); err == nil {
		t.Fatal("invalid weight accepted")
	}
}

func TestNotACache(t *testing.T) {
	if n := NotACache(); n != 2 {
		t.Fatal(n)
	}
}
func TestWaitCancelsEvenSuccess(t *testing.T) {
	g, ctx := errgroup.WithContext(context.Background())
	g.Go(func() error { return nil })
	if err := g.Wait(); err != nil {
		t.Fatal(err)
	}
	if !errors.Is(ctx.Err(), context.Canceled) {
		t.Fatal(ctx.Err())
	}
}

func TestTryGoAtCapacity(t *testing.T) {
	if TryGoAtCapacity() {
		t.Fatal("exceeded limit")
	}
}
func TestAbandonWaitDoesNotCancelSharedWork(t *testing.T) {
	var g singleflight.Group
	entered, release := make(chan struct{}), make(chan struct{})
	a := g.DoChan("k", func() (any, error) { close(entered); <-release; return "value", nil })
	<-entered
	b := g.DoChan("k", func() (any, error) { t.Error("duplicate executed"); return nil, nil })
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	select {
	case <-a:
		t.Fatal("work completed before release")
	case <-ctx.Done():
	}
	close(release)
	r := <-b
	if r.Val != "value" || r.Err != nil || !r.Shared {
		t.Fatal(r)
	}
	// Receiving once is valid; the result channel is not closed afterward.
	<-a
	select {
	case _, ok := <-a:
		if !ok {
			t.Fatal("DoChan result channel closed")
		}
	default:
	}
}
