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

🤗 Testing a gofiber proxy handler #1929

Open
dominicfollett opened this issue Jun 3, 2022 · 1 comment
Open

🤗 Testing a gofiber proxy handler #1929

dominicfollett opened this issue Jun 3, 2022 · 1 comment

Comments

@dominicfollett
Copy link

Question description
Hi there, thank you for the awesome Gofiber package. I have a question about testing Gofiber as a proxy. Small caveat: I am somewhat new to Go and Gofiber.

I have implemented a small proxy service using Gofiber, and I used your tests here as reference for writing my own tests.

I'd like to know if this the recommend or correct approach to testing Gofiber as a proxy? Or if I should be mocking out the proxying somehow.

Handler Function

func BulkEventsHandler(c *fiber.Ctx) error {
	url := buildURL(cfg().Proxy.Forward.Events, c.Path())

	// Before we do the proxy save the body to a DB
	// in a goroutine so that it doesn't block
	go saveEvent(copyBody(c.Request().Body()))

	if err := proxy.Do(c, url); err != nil {
		logrus.Error("Proxy to Foo failed: ", err)
		return err
	}

	// Remove Server header from response
	c.Response().Header.Del(fiber.HeaderServer)
	return nil
}

TestFunction

func TestBulkEventsHandler(t *testing.T) {
	// Parallel signals that this test is to be run in parallel
	// with (and only with) other parallel tests.
	t.Parallel()

	// Create a new fiber app
	app := fiber.New()

	// Make sure the proxy middleware doesn't have problems with
	// self-signed certificates
	proxy.WithTlsConfig(&tls.Config{
		InsecureSkipVerify: true,
	})

	// Create a fake server for handling the proxied request
	_, addr := createProxyTestServer(
		func(c *fiber.Ctx) error {
			return c.SendString("forwarded")
		}, t, "/events/bulk/:id",
	)

	// Mock out the function that returns config - there must be a better way
	// Adjust the config so that we're forwarding requests to our new server
	cfg = func() config.Config {
		return config.Config{
			Proxy: config.Proxy{
				Forward: config.Forward{
					Events: addr,
				},
			},
		}
	}

	// Set up the route, and specify the handler under test
	app.Post("/events/bulk/:id", BulkEventsHandler)

	// Create some dummy event data
	bodyReader := strings.NewReader(`{"user": "12124", "data": "blah"}`)

	// Test our proxy
	resp, err := app.Test(
		httptest.NewRequest(
			"POST",
			"/events/bulk/1234",
			bodyReader,
		),
	)

	utils.AssertEqual(t, nil, err)
	utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)

	b, err := ioutil.ReadAll(resp.Body)
	utils.AssertEqual(t, nil, err)
	utils.AssertEqual(t, "forwarded", string(b))
}

Is the following necessary? Should I instead somehow mock proxy.Do?

Server that handles the proxied request

func createProxyTestServer(handler fiber.Handler, t *testing.T, path string) (*fiber.App, string) {
	// Helper marks the calling function as a test helper function.
	t.Helper()

	// Create a new fiber app that will act as the server
	target := fiber.New(fiber.Config{DisableStartupMessage: true})
	// Set up the route that we will expect to be proxied to us
	target.Post(path, handler)

	// Create a listener on a random unused port
	ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0")
	utils.AssertEqual(t, nil, err)

	// Generate self-signed certs for this server
	serverTLSConf, _, err := getTLSConfigs()
	utils.AssertEqual(t, nil, err)

	// Update the listener with the certs
	ln = tls.NewListener(ln, serverTLSConf)

	// This has to be run in a goroutine otherwise it will block
	go func() {
		// set the app's listener
		utils.AssertEqual(t, nil, target.Listener(ln))
	}()

	// Give the app time to get ready - this isn't ideal
	time.Sleep(2 * time.Second)
	addr := ln.Addr().String()

	return target, addr
}

Additionally, I'd like to test that the saveEvent method in my handler gets called, but the only way I think I could do that is if I altered the call signature of the handler e.g. func(c * fiber.Ctx, db *DB) where I could pass in a mock instead. But I don't think that is possible. Any recommendation would be most welcome. Thank you.

@welcome
Copy link

welcome bot commented Jun 3, 2022

Thanks for opening your first issue here! 🎉 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants