Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix flaky server input test #198

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Test
# -count=2 ensures that test fixtures cleanup after themselves
# because any leftover state will generally cause the second run to fail.
run: go test -shuffle=on -count=2 -race -cover -v ./...
run: go test -shuffle=on -count=2 -race -cover -v -timeout=5m ./...

lint:
runs-on: ubuntu-latest
Expand Down
7 changes: 6 additions & 1 deletion cmd/dependabot/internal/cmd/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"log"
"net"
"os"

"github.com/MakeNowJust/heredoc"
Expand Down Expand Up @@ -140,7 +141,11 @@ func extractInput(cmd *cobra.Command, flags *UpdateFlags) (*model.Input, error)
}

if hasServer {
return server.Input(flags.inputServerPort)
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", flags.inputServerPort))
if err != nil {
return nil, fmt.Errorf("failed to create listener: %w", err)
}
return server.Input(l)
}

if hasStdin {
Expand Down
25 changes: 11 additions & 14 deletions internal/server/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package server
import (
"context"
"encoding/json"
"fmt"
"errors"
"github.com/dependabot/cli/internal/model"
"log"
"net"
"net/http"
"time"

"github.com/dependabot/cli/internal/model"
)

type credServer struct {
Expand All @@ -29,17 +28,15 @@ func (s *credServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

// Input receives configuration via HTTP on the port and returns it decoded
func Input(port int) (*model.Input, error) {
server := &http.Server{
Addr: fmt.Sprintf("127.0.0.1:%d", port),
ReadHeaderTimeout: time.Second,
}
s := &credServer{server: server}
server.Handler = s
func Input(listener net.Listener) (*model.Input, error) {
handler := &credServer{}
srv := &http.Server{Handler: handler}
handler.server = srv

// printing so the user doesn't think the cli is hanging
log.Println("waiting for input on port", port)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Println("waiting for input on", listener.Addr())
if err := srv.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
return nil, err
}
return s.data, nil
return handler.data, nil
}
40 changes: 27 additions & 13 deletions internal/server/input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,48 @@ package server

import (
"bytes"
"fmt"
"github.com/dependabot/cli/internal/model"
"net"
"net/http"
"sync"
"os"
"testing"
"time"

"github.com/dependabot/cli/internal/model"
)

func TestInput(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(1)
var input *model.Input
inputCh := make(chan *model.Input)
defer close(inputCh)

ip := ""
// prevents security popup
if os.Getenv("GOOS") == "darwin" {
ip = "127.0.0.1"
}
l, err := net.Listen("tcp", ip+":0")
if err != nil {
t.Fatal("Failed to create listener: ", err.Error())
}

go func() {
input, _ = Input(8080)
wg.Done()
input, err := Input(l)
if err != nil {
t.Errorf(err.Error())
}
inputCh <- input
}()
// give the server time to start
time.Sleep(10 * time.Millisecond)

url := fmt.Sprintf("http://%s", l.Addr().String())
data := `{"job":{"package-manager":"test"},"credentials":[{"credential":"value"}]}`
resp, err := http.Post("http://localhost:8080", "application/json", bytes.NewReader([]byte(data)))
resp, err := http.Post(url, "application/json", bytes.NewReader([]byte(data)))
if err != nil {
t.Fatal(err.Error())
}
if resp.StatusCode != 200 {
t.Errorf("expected status code 200, got %d", resp.StatusCode)
}
wg.Wait()

// Test will hang here if the server does not shut down
input := <-inputCh

if input.Job.PackageManager != "test" {
t.Errorf("expected package manager to be 'test', got '%s'", input.Job.PackageManager)
Expand Down