// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/server_test.go.tmpl

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package semconv

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"go.opentelemetry.io/otel/attribute"
)

func TestHTTPServer_MetricAttributes(t *testing.T) {
	defaultRequest := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "http://example.com/path?query=test", http.NoBody)
	reqWithPattern := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/path/abc123", http.NoBody)
	reqWithPattern.Pattern = "/path/abc123"

	tests := []struct {
		name                 string
		server               string
		req                  *http.Request
		statusCode           int
		route                string
		additionalAttributes []attribute.KeyValue
		wantFunc             func(t *testing.T, attrs []attribute.KeyValue)
	}{
		{
			name:                 "routine testing",
			server:               "",
			req:                  defaultRequest,
			statusCode:           200,
			route:                "",
			additionalAttributes: []attribute.KeyValue{attribute.String("test", "test")},
			wantFunc: func(t *testing.T, attrs []attribute.KeyValue) {
				require.Len(t, attrs, 7)
				assert.ElementsMatch(t, []attribute.KeyValue{
					attribute.String("http.request.method", "GET"),
					attribute.String("url.scheme", "http"),
					attribute.String("server.address", "example.com"),
					attribute.String("network.protocol.name", "http"),
					attribute.String("network.protocol.version", "1.1"),
					attribute.Int64("http.response.status_code", 200),
					attribute.String("test", "test"),
				}, attrs)
			},
		},
		{
			name:                 "use server address",
			server:               "example.com:9999",
			req:                  defaultRequest,
			statusCode:           200,
			route:                "/path/${id}",
			additionalAttributes: nil,
			wantFunc: func(t *testing.T, attrs []attribute.KeyValue) {
				require.Len(t, attrs, 8)
				assert.ElementsMatch(t, []attribute.KeyValue{
					attribute.String("http.request.method", "GET"),
					attribute.String("url.scheme", "http"),
					attribute.String("server.address", "example.com"),
					attribute.Int("server.port", 9999),
					attribute.String("network.protocol.name", "http"),
					attribute.String("network.protocol.version", "1.1"),
					attribute.Int64("http.response.status_code", 200),
					attribute.String("http.route", "/path/${id}"),
				}, attrs)
			},
		},
		{
			name: "use route from request pattern",
			req:  reqWithPattern,
			wantFunc: func(t *testing.T, attrs []attribute.KeyValue) {
				require.Len(t, attrs, 6)
				assert.ElementsMatch(t, []attribute.KeyValue{
					attribute.String("http.request.method", "GET"),
					attribute.String("url.scheme", "http"),
					attribute.String("server.address", "example.com"),
					attribute.String("network.protocol.name", "http"),
					attribute.String("network.protocol.version", "1.1"),
					attribute.String("http.route", "/path/abc123"),
				}, attrs)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := HTTPServer{}.MetricAttributes(tt.server, tt.req, tt.statusCode, tt.route, tt.additionalAttributes)
			tt.wantFunc(t, got)
		})
	}
}

func TestNewMethod(t *testing.T) {
	testCases := []struct {
		method   string
		n        int
		want     attribute.KeyValue
		wantOrig attribute.KeyValue
	}{
		{
			method: http.MethodPost,
			n:      1,
			want:   attribute.String("http.request.method", "POST"),
		},
		{
			method:   "",
			n:        2,
			want:     attribute.String("http.request.method", "_OTHER"),
			wantOrig: attribute.KeyValue{},
		},
		{
			method:   "Put",
			n:        3,
			want:     attribute.String("http.request.method", "PUT"),
			wantOrig: attribute.String("http.request.method_original", "Put"),
		},
		{
			method:   "Unknown",
			n:        4,
			want:     attribute.String("http.request.method", "_OTHER"),
			wantOrig: attribute.String("http.request.method_original", "Unknown"),
		},
	}

	for _, tt := range testCases {
		t.Run(tt.method, func(t *testing.T) {
			got, gotOrig := HTTPServer{}.method(tt.method)
			assert.Equal(t, tt.want, got)
			assert.Equal(t, tt.wantOrig, gotOrig)
		})
	}
}

func TestRequestTraceAttrs_HTTPRoute(t *testing.T) {
	tests := []struct {
		name      string
		pattern   string
		wantRoute string
	}{
		{
			name:      "only path",
			pattern:   "/path/{id}",
			wantRoute: "/path/{id}",
		},
		{
			name:      "with method",
			pattern:   "GET /path/{id}",
			wantRoute: "/path/{id}",
		},
		{
			name:      "with domain",
			pattern:   "example.com/path/{id}",
			wantRoute: "/path/{id}",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/path/abc123", http.NoBody)
			req.Pattern = tt.pattern

			attrs := (HTTPServer{}).RequestTraceAttrs("", req, RequestTraceAttrsOpts{})

			var gotRoute string
			for _, attr := range attrs {
				if attr.Key == "http.route" {
					gotRoute = attr.Value.AsString()
					break
				}
			}
			require.Equal(t, tt.wantRoute, gotRoute)
		})
	}
}

func TestRequestTraceAttrs_ClientIP(t *testing.T) {
	for _, tt := range []struct {
		name              string
		requestModifierFn func(r *http.Request)
		requestTraceOpts  RequestTraceAttrsOpts

		wantClientIP string
	}{
		{
			name:         "with a client IP from the network",
			wantClientIP: "1.2.3.4",
		},
		{
			name: "with a client IP from x-forwarded-for header",
			requestModifierFn: func(r *http.Request) {
				r.Header.Add("X-Forwarded-For", "5.6.7.8")
			},
			wantClientIP: "5.6.7.8",
		},
		{
			name: "with a client IP in options",
			requestModifierFn: func(r *http.Request) {
				r.Header.Add("X-Forwarded-For", "5.6.7.8")
			},
			requestTraceOpts: RequestTraceAttrsOpts{
				HTTPClientIP: "9.8.7.6",
			},
			wantClientIP: "9.8.7.6",
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/example", http.NoBody)
			req.RemoteAddr = "1.2.3.4:5678"

			if tt.requestModifierFn != nil {
				tt.requestModifierFn(req)
			}

			var found bool
			for _, attr := range (HTTPServer{}).RequestTraceAttrs("", req, tt.requestTraceOpts) {
				if attr.Key != "client.address" {
					continue
				}
				found = true
				assert.Equal(t, tt.wantClientIP, attr.Value.AsString())
			}
			require.True(t, found)
		})
	}
}

func TestHTTPServer_SpanName(t *testing.T) {
	tests := []struct {
		name     string
		method   string
		pattern  string
		wantName string
	}{
		{
			name:     "standard method, no pattern",
			method:   http.MethodGet,
			wantName: "GET",
		},
		{
			name:     "standard method, path-only pattern",
			method:   http.MethodGet,
			pattern:  "/foo/{id}",
			wantName: "GET /foo/{id}",
		},
		{
			name:     "standard method, pattern with method prefix",
			method:   http.MethodGet,
			pattern:  "GET /foo/{id}",
			wantName: "GET /foo/{id}",
		},
		{
			name:     "standard method, pattern with host prefix",
			method:   http.MethodGet,
			pattern:  "example.com/foo/{id}",
			wantName: "GET /foo/{id}",
		},
		{
			name:     "nonstandard method, no pattern",
			method:   "CUSTOM",
			wantName: "HTTP",
		},
		{
			name:     "nonstandard method, with pattern",
			method:   "CUSTOM",
			pattern:  "/foo/{id}",
			wantName: "HTTP /foo/{id}",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequestWithContext(t.Context(), tt.method, "/foo/123", http.NoBody)
			req.Pattern = tt.pattern

			got := HTTPServer{}.SpanName(req)
			assert.Equal(t, tt.wantName, got)
		})
	}
}
