Ctrl K
ComunidadGuías…

codersdk/agentsdk/agentsdk_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package agentsdk_test

import (
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/require"
	"tailscale.com/tailcfg"

	"cdr.dev/slog/v3/sloggers/slogtest"
	"github.com/coder/coder/v2/codersdk/agentsdk"
	"github.com/coder/coder/v2/testutil"
)

// newReinitTestClient returns an http.Client with a dedicated transport.
//
// The tests must not use &http.Client{} (which shares the process-global
// http.DefaultTransport with every other parallel test in the binary).
// httptest.Server.Close() calls http.DefaultTransport.CloseIdleConnections(),
// so a server closed by an unrelated parallel test can break an in-flight
// request here with "http: CloseIdleConnections called". A dedicated transport
// is not shared, so those cross-test calls cannot affect it. See
// coder/internal#1451.
func newReinitTestClient() *http.Client {
	return &http.Client{Transport: &http.Transport{}}
}

func TestStreamAgentReinitEvents(t *testing.T) {
	t.Parallel()

	t.Run("transmitted events are received", func(t *testing.T) {
		t.Parallel()

		eventToSend := agentsdk.ReinitializationEvent{
			WorkspaceID: uuid.New(),
			Reason:      agentsdk.ReinitializeReasonPrebuildClaimed,
			OwnerID:     uuid.New(),
		}

		events := make(chan agentsdk.ReinitializationEvent, 1)
		events <- eventToSend

		transmitCtx := testutil.Context(t, testutil.WaitShort)
		transmitErrCh := make(chan error, 1)
		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
			transmitErrCh <- transmitter.Transmit(transmitCtx, events)
		}))
		defer srv.Close()

		requestCtx := testutil.Context(t, testutil.WaitShort)
		req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
		require.NoError(t, err)
		client := newReinitTestClient()
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()

		receiveCtx := testutil.Context(t, testutil.WaitShort)
		receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
		sentEvent, receiveErr := receiver.Receive(receiveCtx)
		require.Nil(t, receiveErr)
		require.Equal(t, eventToSend, *sentEvent)
	})

	t.Run("doesn't transmit events if the transmitter context is canceled", func(t *testing.T) {
		t.Parallel()

		eventToSend := agentsdk.ReinitializationEvent{
			WorkspaceID: uuid.New(),
			Reason:      agentsdk.ReinitializeReasonPrebuildClaimed,
		}

		events := make(chan agentsdk.ReinitializationEvent, 1)
		events <- eventToSend

		transmitCtx, cancelTransmit := context.WithCancel(testutil.Context(t, testutil.WaitShort))
		cancelTransmit()
		transmitErrCh := make(chan error, 1)
		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
			transmitErrCh <- transmitter.Transmit(transmitCtx, events)
		}))

		defer srv.Close()

		requestCtx := testutil.Context(t, testutil.WaitShort)
		req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
		require.NoError(t, err)
		client := newReinitTestClient()
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()

		receiveCtx := testutil.Context(t, testutil.WaitShort)
		receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
		sentEvent, receiveErr := receiver.Receive(receiveCtx)
		require.Nil(t, sentEvent)
		require.ErrorIs(t, receiveErr, io.EOF)
	})

	t.Run("does not receive events if the receiver context is canceled", func(t *testing.T) {
		t.Parallel()

		eventToSend := agentsdk.ReinitializationEvent{
			WorkspaceID: uuid.New(),
			Reason:      agentsdk.ReinitializeReasonPrebuildClaimed,
		}

		events := make(chan agentsdk.ReinitializationEvent, 1)
		events <- eventToSend

		transmitCtx := testutil.Context(t, testutil.WaitShort)
		transmitErrCh := make(chan error, 1)
		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			transmitter := agentsdk.NewSSEAgentReinitTransmitter(slogtest.Make(t, nil), w, r)
			transmitErrCh <- transmitter.Transmit(transmitCtx, events)
		}))
		defer srv.Close()

		requestCtx := testutil.Context(t, testutil.WaitShort)
		req, err := http.NewRequestWithContext(requestCtx, "GET", srv.URL, nil)
		require.NoError(t, err)
		client := newReinitTestClient()
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer resp.Body.Close()

		receiveCtx, cancelReceive := context.WithCancel(context.Background())
		cancelReceive()
		receiver := agentsdk.NewSSEAgentReinitReceiver(resp.Body)
		sentEvent, receiveErr := receiver.Receive(receiveCtx)
		require.Nil(t, sentEvent)
		require.ErrorIs(t, receiveErr, context.Canceled)
	})
}

func TestRewriteDERPMap(t *testing.T) {
	t.Parallel()
	// This test ensures that RewriteDERPMap mutates built-in DERPs with the
	// client access URL.
	dm := &tailcfg.DERPMap{
		Regions: map[int]*tailcfg.DERPRegion{
			1: {
				EmbeddedRelay: true,
				RegionID:      1,
				Nodes: []*tailcfg.DERPNode{{
					HostName: "bananas.org",
					DERPPort: 1,
				}},
			},
		},
	}
	parsed, err := url.Parse("https://coconuts.org:44558")
	require.NoError(t, err)
	client := agentsdk.New(parsed, agentsdk.WithFixedToken("unused"))
	client.RewriteDERPMap(dm)
	region := dm.Regions[1]
	require.True(t, region.EmbeddedRelay)
	require.Len(t, region.Nodes, 1)
	node := region.Nodes[0]
	require.Equal(t, "coconuts.org", node.HostName)
	require.Equal(t, 44558, node.DERPPort)
}

func TestExternalAuthRequestQuery(t *testing.T) {
	t.Parallel()

	t.Run("IncludesGitRefFieldsAndOmitsWorkdir", func(t *testing.T) {
		t.Parallel()

		srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			require.Equal(t, "/api/v2/workspaceagents/me/external-auth", r.URL.Path)
			require.Equal(t, "true", r.URL.Query().Get("listen"))
			require.Equal(t, "main", r.URL.Query().Get("git_branch"))
			require.Equal(t, "https://github.com/coder/coder.git", r.URL.Query().Get("git_remote_origin"))
			require.Equal(t, "test-chat-id", r.URL.Query().Get("chat_id"))
			require.False(t, r.URL.Query().Has("workdir"))
			_, _ = w.Write([]byte(`{"type":"github","access_token":"token"}`))
		}))
		defer srv.Close()

		parsedURL, err := url.Parse(srv.URL)
		require.NoError(t, err)

		client := agentsdk.New(parsedURL, agentsdk.WithFixedToken("token"))
		_, err = client.ExternalAuth(testutil.Context(t, testutil.WaitShort), agentsdk.ExternalAuthRequest{
			Match:           "github.com",
			Listen:          true,
			GitBranch:       "main",
			GitRemoteOrigin: "https://github.com/coder/coder.git",
			ChatID:          "test-chat-id",
		})
		require.NoError(t, err)
	})
}