ronantakizawa/codereview-bench huggingface dataset:
Repository: prometheus/alertmanager
File: ui/web_test.go (Go)

Code before:
	"log/slog"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/prometheus/common/route"
	"github.com/stretchr/testify/require"
)

func TestDebugHandlersWithRoutePrefix(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
	reloadCh := make(chan chan error)

	// Test with route prefix
	routePrefix := "/prometheus/alertmanager"
	router := route.New().WithPrefix(routePrefix)
	Register(router, reloadCh, logger)

	// Test GET request to pprof index (note: pprof index returns text/html)
	req := httptest.NewRequest("GET", routePrefix+"/debug/pprof/", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
	require.NotEqual(t, 0, w.Body.Len())

	// Test GET request to pprof heap endpoint
	req = httptest.NewRequest("GET", routePrefix+"/debug/pprof/heap", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	// Test without route prefix (should also work)
	router2 := route.New()
	Register(router2, reloadCh, logger)

	req = httptest.NewRequest("GET", "/debug/pprof/", nil)
	w = httptest.NewRecorder()
	router2.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}


Review comment (suggestion):
```suggestion
	require.Positive(t, w.Body.Len(), "body should not be empty")
```

Alternatively we could replace the checks with https://pkg.go.dev/github.com/stretchr/testify/require#Assertions.HTTPBodyContains
but feel free to skip it.

Code after:
	"log/slog"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/prometheus/common/route"
	"github.com/stretchr/testify/require"
)

func TestDebugHandlersWithRoutePrefix(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
	reloadCh := make(chan chan error)

	// Test with route prefix
	routePrefix := "/prometheus/alertmanager"
	router := route.New().WithPrefix(routePrefix)
	Register(router, reloadCh, logger)

	// Test GET request to pprof index (note: pprof index returns text/html)
	req := httptest.NewRequest("GET", routePrefix+"/debug/pprof/", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
	require.Contains(t, w.Body.String(), "/debug/pprof/", "pprof page did not load with expected content when using a route prefix")

	// Test GET request to pprof heap endpoint
	req = httptest.NewRequest("GET", routePrefix+"/debug/pprof/heap", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	// Test without route prefix (should also work)
	router2 := route.New()
	Register(router2, reloadCh, logger)

	req = httptest.NewRequest("GET", "/debug/pprof/", nil)
	w = httptest.NewRecorder()
	router2.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
	require.Contains(t, w.Body.String(), "/debug/pprof/", "pprof page did not load with expected content")
}
Repository: prometheus/alertmanager
File: dispatch/dispatch.go (Go)

Code before:
// See the License for the specific language governing permissions and
// limitations under the License.

package dispatch

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"sort"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/store"
	"github.com/prometheus/alertmanager/types"
)

const (
	DISPATCHER_STATE_UNKNOWN = iota
	DISPATCHER_STATE_STARTING
	DISPATCHER_STATE_RUNNING
)

// DispatcherMetrics represents metrics associated to a dispatcher.
type DispatcherMetrics struct {
	aggrGroups            prometheus.Gauge
	processingDuration    prometheus.Summary
	aggrGroupLimitReached prometheus.Counter
}

// NewDispatcherMetrics returns a new registered DispatchMetrics.
func NewDispatcherMetrics(registerLimitMetrics bool, r prometheus.Registerer) *DispatcherMetrics {
	if r == nil {
		return nil
	}
	m := DispatcherMetrics{
		aggrGroups: promauto.With(r).NewGauge(
			prometheus.GaugeOpts{
				Name: "alertmanager_dispatcher_aggregation_groups",
				Help: "Number of active aggregation groups",
			},
		),
		processingDuration: promauto.With(r).NewSummary(
			prometheus.SummaryOpts{

Review comment (suggestion):
small style nitpick: constants should be `MixedCaps`
```suggestion
	DispatcherStateUnknown = iota
	DispatcherStateStarting
	DispatcherStateRunning
```

Code after:
// See the License for the specific language governing permissions and
// limitations under the License.

package dispatch

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"sort"
	"sync"
	"sync/atomic"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"

	"github.com/prometheus/alertmanager/notify"
	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/store"
	"github.com/prometheus/alertmanager/types"
)

const (
	DispatcherStateUnknown = iota
	DispatcherStateWaitingToStart
	DispatcherStateRunning
)

// DispatcherMetrics represents metrics associated to a dispatcher.
type DispatcherMetrics struct {
	aggrGroups            prometheus.Gauge
	processingDuration    prometheus.Summary
	aggrGroupLimitReached prometheus.Counter
}

// NewDispatcherMetrics returns a new registered DispatchMetrics.
func NewDispatcherMetrics(registerLimitMetrics bool, r prometheus.Registerer) *DispatcherMetrics {
	if r == nil {
		return nil
	}
	m := DispatcherMetrics{
		aggrGroups: promauto.With(r).NewGauge(
			prometheus.GaugeOpts{
				Name: "alertmanager_dispatcher_aggregation_groups",
				Help: "Number of active aggregation groups",
			},
		),
		processingDuration: promauto.With(r).NewSummary(
Repository: prometheus/alertmanager
File: silence/silence.go (Go)

Code before:
	case types.SilenceStatePending:
		// Set both to now to make Silence move to "expired" state
		sil.StartsAt = now
		sil.EndsAt = now
	}
	sil.UpdatedAt = now
	return s.setSilence(s.toMeshSilence(sil), now)
}

// QueryParam expresses parameters along which silences are queried.
type QueryParam func(*query) error

type query struct {
	ids     []string
	filters []silenceFilter
}

// silenceFilter is a function that returns true if a silence
// should be dropped from a result set for a given time.
type silenceFilter func(*pb.Silence, *Silences, time.Time) (bool, error)

// QIDs configures a query to select the given silence IDs.
func QIDs(ids ...string) QueryParam {
	return func(q *query) error {
		if len(ids) == 0 {
			return fmt.Errorf("QIDs filter must have at least one id")
		}
		q.ids = append(q.ids, ids...)
		return nil
	}
}

// QMatches returns silences that match the given label set.
func QMatches(set model.LabelSet) QueryParam {
	return func(q *query) error {
		f := func(sil *pb.Silence, s *Silences, _ time.Time) (bool, error) {
			m, err := s.mi.get(sil)
			if err != nil {
				return true, err
			}
			return m.Matches(set), nil
		}
		q.filters = append(q.filters, f)
		return nil
	}
}

// getState returns a silence's SilenceState at the given timestamp.
func getState(sil *pb.Silence, ts time.Time) types.SilenceState {
	if ts.Before(sil.StartsAt) {
		return types.SilenceStatePending

Review comment (suggestion):
Please use `errors` package here.

```suggestion
			return errors.New("QIDs filter must have at least one id")
```

Code after:
	case types.SilenceStatePending:
		// Set both to now to make Silence move to "expired" state
		sil.StartsAt = now
		sil.EndsAt = now
	}
	sil.UpdatedAt = now
	return s.setSilence(s.toMeshSilence(sil), now)
}

// QueryParam expresses parameters along which silences are queried.
type QueryParam func(*query) error

type query struct {
	ids     []string
	filters []silenceFilter
}

// silenceFilter is a function that returns true if a silence
// should be dropped from a result set for a given time.
type silenceFilter func(*pb.Silence, *Silences, time.Time) (bool, error)

// QIDs configures a query to select the given silence IDs.
func QIDs(ids ...string) QueryParam {
	return func(q *query) error {
		if len(ids) == 0 {
			return errors.New("QIDs filter must have at least one id")
		}
		q.ids = append(q.ids, ids...)
		return nil
	}
}

// QMatches returns silences that match the given label set.
func QMatches(set model.LabelSet) QueryParam {
	return func(q *query) error {
		f := func(sil *pb.Silence, s *Silences, _ time.Time) (bool, error) {
			m, err := s.mi.get(sil)
			if err != nil {
				return true, err
			}
			return m.Matches(set), nil
		}
		q.filters = append(q.filters, f)
		return nil
	}
}

// getState returns a silence's SilenceState at the given timestamp.
func getState(sil *pb.Silence, ts time.Time) types.SilenceState {
	if ts.Before(sil.StartsAt) {
		return types.SilenceStatePending
Repository: prometheus/alertmanager
File: silence/silence.go (Go)

Code before:
	if err != nil {
		return err
	}
	// Choose new version index capacity with some growth buffer.
	newVersionIndex := make(versionIndex, 0, max(len(st)*5/4, 1024))
	for _, e := range st {
		// Comments list was moved to a single comment. Upgrade on loading the snapshot.
		if len(e.Silence.Comments) > 0 {
			e.Silence.Comment = e.Silence.Comments[0].Comment
			e.Silence.CreatedBy = e.Silence.Comments[0].Author
			e.Silence.Comments = nil
		}
		// Add to matcher index, and only if successful, to the new state.
		if _, err := s.mi.add(e.Silence); err != nil {
			s.metrics.matcherCompileLoadSnapshotErrorsTotal.Inc()
			s.logger.Error("Failed to compile silence matchers during snapshot load", "silence_id", e.Silence.Id, "err", err)
		} else {
			st[e.Silence.Id] = e

			newVersionIndex.add(s.version+1, e.Silence.Id)
		}
	}
	s.mtx.Lock()
	s.st = st
	s.vi = newVersionIndex
	s.version++
	s.updateSizeMetrics()
	s.mtx.Unlock()

	return nil
}

// Snapshot writes the full internal state into the writer and returns the number of bytes
// written.
func (s *Silences) Snapshot(w io.Writer) (int64, error) {
	start := time.Now()
	defer func() { s.metrics.snapshotDuration.Observe(time.Since(start).Seconds()) }()

	s.mtx.RLock()
	defer s.mtx.RUnlock()

	b, err := s.st.MarshalBinary()
	if err != nil {
		return 0, err
	}

	return io.Copy(w, bytes.NewReader(b))
}

// MarshalBinary serializes all silences.
func (s *Silences) MarshalBinary() ([]byte, error) {

Review comment (suggestion):
This is unrelated to your change. However, I'm wondering if this lock is necessary because it appears `loadSnapshot` is only called during construction of new Silences type. 

Code after:
	if err != nil {
		return err
	}

	mi := make(matcherIndex, len(st)) // for a map, len is ok as a size hint.
	// Choose new version index capacity with some growth buffer.
	vi := make(versionIndex, 0, max(len(st)*5/4, 1024))

	for _, e := range st {
		// Comments list was moved to a single comment. Upgrade on loading the snapshot.
		if len(e.Silence.Comments) > 0 {
			e.Silence.Comment = e.Silence.Comments[0].Comment
			e.Silence.CreatedBy = e.Silence.Comments[0].Author
			e.Silence.Comments = nil
		}
		// Add to matcher index, and only if successful, to the new state.
		if _, err := mi.add(e.Silence); err != nil {
			s.metrics.matcherCompileLoadSnapshotErrorsTotal.Inc()
			s.logger.Error("Failed to compile silence matchers during snapshot load", "silence_id", e.Silence.Id, "err", err)
		} else {
			st[e.Silence.Id] = e

			vi.add(s.version+1, e.Silence.Id)
		}
	}
	s.mtx.Lock()
	s.st = st
	s.mi = mi
	s.vi = vi
	s.version++
	s.updateSizeMetrics()
	s.mtx.Unlock()

	return nil
}

// Snapshot writes the full internal state into the writer and returns the number of bytes
// written.
func (s *Silences) Snapshot(w io.Writer) (int64, error) {
	start := time.Now()
	defer func() { s.metrics.snapshotDuration.Observe(time.Since(start).Seconds()) }()

	s.mtx.RLock()
	defer s.mtx.RUnlock()

	b, err := s.st.MarshalBinary()
	if err != nil {
		return 0, err
	}

	return io.Copy(w, bytes.NewReader(b))
Repository: prometheus/alertmanager
File: config/config.go (Go)

Code before:
		if MarshalSecretValue {
			return err
		}
		return errors.New(strings.ReplaceAll(err.Error(), string(data), "[REDACTED]"))
	}

	return nil
}

// containsTemplating checks if the string contains template syntax.
func containsTemplating(s string) (bool, error) {
	if !strings.Contains(s, "{{") {
		return false, nil
	}
	// If it contains template syntax, validate it's actually a valid templ.
	_, err := template.New("").Parse(s)
	if err != nil {
		return true, err
	}
	return true, nil
}

// SecretTemplURL is a Secret string that represents a URL which may contain
// Go template syntax. Unlike SecretURL, it allows templated values and only
// validates non-templated URLs at unmarshal time.
type SecretTemplURL Secret

// MarshalYAML implements the yaml.Marshaler interface for SecretTemplURL.
func (s SecretTemplURL) MarshalYAML() (any, error) {
	if s != "" {
		if MarshalSecretValue {
			return string(s), nil
		}
		return secretToken, nil
	}
	return nil, nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for SecretTemplURL.
func (s *SecretTemplURL) UnmarshalYAML(unmarshal func(any) error) error {
	type plain Secret
	if err := unmarshal((*plain)(s)); err != nil {
		return err
	}

	urlStr := string(*s)

	// Skip validation for empty strings or secret token
	if urlStr == "" || urlStr == secretToken {
		return nil
	}

Review comment (security):
this doesn't need to block merging this change, but I'd prefer if we named this `SecretTemplateURL` - it's only a few more characters, and I think it's quite a bit more clear to a reader

Code after:
		if MarshalSecretValue {
			return err
		}
		return errors.New(strings.ReplaceAll(err.Error(), string(data), "[REDACTED]"))
	}

	return nil
}

// containsTemplating checks if the string contains template syntax.
func containsTemplating(s string) (bool, error) {
	if !strings.Contains(s, "{{") {
		return false, nil
	}
	// If it contains template syntax, validate it's actually a valid templ.
	_, err := template.New("").Parse(s)
	if err != nil {
		return true, err
	}
	return true, nil
}

// SecretTemplateURL is a Secret string that represents a URL which may contain
// Go template syntax. Unlike SecretURL, it allows templated values and only
// validates non-templated URLs at unmarshal time.
type SecretTemplateURL Secret

// MarshalYAML implements the yaml.Marshaler interface for SecretTemplateURL.
func (s SecretTemplateURL) MarshalYAML() (any, error) {
	if s != "" {
		if MarshalSecretValue {
			return string(s), nil
		}
		return secretToken, nil
	}
	return nil, nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for SecretTemplateURL.
func (s *SecretTemplateURL) UnmarshalYAML(unmarshal func(any) error) error {
	type plain Secret
	if err := unmarshal((*plain)(s)); err != nil {
		return err
	}

	urlStr := string(*s)

	// Skip validation for empty strings or secret token
	if urlStr == "" || urlStr == secretToken {
		return nil
	}
Repository: prometheus/alertmanager
File: provider/mem/mem.go (Go)

Code before:
// Copyright Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mem

import (
	"context"
	"log/slog"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"

Review comment (security):
```suggestion
// Copyright The Prometheus Authors
```

Code after:
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mem

import (
	"context"
	"log/slog"
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/common/model"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
Repository: prometheus/alertmanager
File: ui/mantine-ui/src/data/api.ts (TypeScript)

Code before:
    } catch (error) {
      if (!(error instanceof Error)) {
        throw new Error('Unknown error');
      }

      switch (error.name) {
        case 'TypeError':
          throw new Error('Network error or unable to reach the server');
        case 'SyntaxError':
          throw new Error('Invalid JSON response');
        default:
          throw error;
      }
    }
  };

type QueryOptions = {
  key?: QueryKey;
  path: string;
  params?: Record<string, string>;
  enabled?: boolean;
  refetchInterval?: false | number;
  recordResponseTime?: (time: number) => void;
};

export const useAPIQuery = <T>({
  key,
  path,
  params,
  enabled,
  refetchInterval,
  recordResponseTime,
}: QueryOptions) => {
  return useQuery<T>({
    queryKey: key ?? [API_PATH, path, params],
    retry: false,
    refetchOnWindowFocus: false,
    refetchInterval,
    gcTime: 0,
    enabled,
    queryFn: createQueryFn<T>({ pathPrefix, path, params, recordResponseTime }),
  });
};


Review comment (suggestion):
Maybe you'll also want to copy over `useSuspenseAPIQuery` from Prometheus, at least it's been very handy there for any of the normal pages where you first have to load the page's data and then show the page (not for loading data interactively, like for PromQL queries).

Code after:
    } catch (error) {
      if (!(error instanceof Error)) {
        throw new Error('Unknown error');
      }

      switch (error.name) {
        case 'TypeError':
          throw new Error('Network error or unable to reach the server');
        case 'SyntaxError':
          throw new Error('Invalid JSON response');
        default:
          throw error;
      }
    }
  };

type QueryOptions = {
  key?: QueryKey;
  path: string;
  params?: Record<string, string>;
  enabled?: boolean;
  refetchInterval?: false | number;
  recordResponseTime?: (time: number) => void;
};

export const useAPIQuery = <T>({
  key,
  path,
  params,
  enabled,
  refetchInterval,
  recordResponseTime,
}: QueryOptions) => {
  return useQuery<T>({
    queryKey: key ?? [API_PATH, path, params],
    retry: false,
    refetchOnWindowFocus: false,
    refetchInterval,
    gcTime: 0,
    enabled,
    queryFn: createQueryFn<T>({ pathPrefix, path, params, recordResponseTime }),
  });
};

export const useSuspenseAPIQuery = <T>({ key, path, params }: QueryOptions) => {
  return useSuspenseQuery<T>({
    queryKey: key !== undefined ? key : [path, params],
    retry: false,
    refetchOnWindowFocus: false,
    gcTime: 0,
    queryFn: createQueryFn({ pathPrefix, path, params }),
Repository: prometheus/alertmanager
File: dispatch/dispatch.go (Go)

Code before:
		timeout:             timeout,
		maintenanceInterval: maintenanceInterval,
		logger:              logger.With("component", "dispatcher"),
		metrics:             metrics,
		limits:              limits,
		propagator:          otel.GetTextMapPropagator(),
	}
	disp.state.Store(DispatcherStateUnknown)
	disp.loadingFinished.Add(1)
	disp.ctx, disp.cancel = context.WithCancel(context.Background())
	return disp
}

// Run starts dispatching alerts incoming via the updates channel.
func (d *Dispatcher) Run(dispatchStartTime time.Time) {
	if !d.state.CompareAndSwap(DispatcherStateUnknown, DispatcherStateWaitingToStart) {
		return
	}
	d.finished.Add(1)
	defer d.finished.Done()

	d.logger.Debug("preparing to start", "startTime", dispatchStartTime)
	d.startTimer = time.NewTimer(time.Until(dispatchStartTime))
	d.logger.Debug("setting state", "state", "waiting_to_start")
	d.routeGroupsSlice = make([]routeAggrGroups, d.route.Idx+1)
	d.route.Walk(func(r *Route) {
		d.routeGroupsSlice[r.Idx] = routeAggrGroups{
			route: r,
		}
	})

	d.aggrGroupsNum.Store(0)
	d.metrics.aggrGroups.Set(0)

	initalAlerts, it := d.alerts.SlurpAndSubscribe("dispatcher")
	for _, alert := range initalAlerts {
		d.routeAlert(d.ctx, alert)
	}
	d.loadingFinished.Done()

	d.run(it)
}

func (d *Dispatcher) run(it provider.AlertIterator) {
	maintenance := time.NewTicker(d.maintenanceInterval)
	defer maintenance.Stop()

	defer it.Close()

	for {
		select {

Review comment (suggestion):
I guess route index is zero here always, so should we just use `1` for clarity?

```suggestion
	d.routeGroupsSlice = make([]routeAggrGroups, 1)
```

Code after:
		marker:              marker,
		timeout:             timeout,
		maintenanceInterval: maintenanceInterval,
		logger:              logger.With("component", "dispatcher"),
		metrics:             metrics,
		limits:              limits,
		propagator:          otel.GetTextMapPropagator(),
	}
	disp.state.Store(DispatcherStateUnknown)
	disp.loaded = make(chan struct{})
	disp.ctx, disp.cancel = context.WithCancel(context.Background())
	return disp
}

// Run starts dispatching alerts incoming via the updates channel.
func (d *Dispatcher) Run(dispatchStartTime time.Time) {
	if !d.state.CompareAndSwap(DispatcherStateUnknown, DispatcherStateWaitingToStart) {
		return
	}
	d.finished.Add(1)
	defer d.finished.Done()

	d.logger.Debug("preparing to start", "startTime", dispatchStartTime)
	d.startTimer = time.NewTimer(time.Until(dispatchStartTime))
	d.logger.Debug("setting state", "state", "waiting_to_start")
	d.routeGroupsSlice = make([]routeAggrGroups, d.route.Idx+1)
	d.route.Walk(func(r *Route) {
		d.routeGroupsSlice[r.Idx] = routeAggrGroups{
			route: r,
		}
	})

	d.aggrGroupsNum.Store(0)
	d.metrics.aggrGroups.Set(0)

	initalAlerts, it := d.alerts.SlurpAndSubscribe("dispatcher")
	for _, alert := range initalAlerts {
		d.routeAlert(d.ctx, alert)
	}
	close(d.loaded)

	d.run(it)
}

func (d *Dispatcher) run(it provider.AlertIterator) {
	maintenance := time.NewTicker(d.maintenanceInterval)
	defer maintenance.Stop()

	defer it.Close()

	for {