// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package unittest

import (
	"testing"

	"github.com/stretchr/testify/require"
	"xorm.io/xorm/contexts"
)

func TestFaultInjector(t *testing.T) {
	faultInjector := faultInjectorHook{}
	c := &contexts.ContextHook{
		Ctx: t.Context(),
		SQL: "Hello, 世界", // We don't check for valid SQL anyway.
	}

	t.Run("Should not block", func(t *testing.T) {
		// Currently no fault injection is set, so this should go through.
		for range 100 {
			_, err := faultInjector.BeforeProcess(c)
			require.NoError(t, err)
		}
	})

	t.Run("Reset", func(t *testing.T) {
		// Okay only allow one query to go through.
		reset := SetFaultInjector(1)

		// Do the only query.
		_, err := faultInjector.BeforeProcess(c)
		require.NoError(t, err)

		// Now we reset, we don't check the blocking behavior yet. We first
		// must know that we can safely reset.
		reset()

		// This should go through.
		for range 100 {
			_, err := faultInjector.BeforeProcess(c)
			require.NoError(t, err)
		}
	})

	t.Run("Blocking", func(t *testing.T) {
		// Okay only allow one query to go through.
		reset := SetFaultInjector(1)

		// Do the only query.
		_, err := faultInjector.BeforeProcess(c)
		require.NoError(t, err)

		// Any query now will return a error.
		for range 100 {
			_, err := faultInjector.BeforeProcess(c)
			require.ErrorIs(t, err, ErrFaultInjected)
		}

		// Ah but there's a exemption for `ROLLBACK`.
		_, err = faultInjector.BeforeProcess(&contexts.ContextHook{Ctx: t.Context(), SQL: "ROLLBACK"})
		require.NoError(t, err)

		reset()
	})

	t.Run("Number of queries", func(t *testing.T) {
		// For funsies lets test a bunch of max numbers of queries.
		for i := range int64(1024) {
			// Allow i queries
			reset := SetFaultInjector(i)

			// Make i queries.
			for range i {
				_, err := faultInjector.BeforeProcess(c)
				require.NoError(t, err)
			}

			// After i'th query it returns a error.
			_, err := faultInjector.BeforeProcess(c)
			require.ErrorIs(t, err, ErrFaultInjected)

			reset()
		}
	})
}
