client.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package lapitest
  4. import (
  5. "context"
  6. "testing"
  7. "tailscale.com/client/local"
  8. "tailscale.com/ipn"
  9. "tailscale.com/ipn/ipnauth"
  10. )
  11. // Client wraps a [local.Client] for testing purposes.
  12. // It can be created using [Server.Client], [Server.ClientWithName],
  13. // or [Server.ClientFor] and sends requests as the specified actor
  14. // to the associated [Server].
  15. type Client struct {
  16. tb testing.TB
  17. // Client is the underlying [local.Client] wrapped by the test client.
  18. // It is configured to send requests to the test server on behalf of the actor.
  19. *local.Client
  20. // Actor represents the user on whose behalf this client is making requests.
  21. // The server uses it to determine the client's identity and permissions.
  22. // The test can mutate the user to alter the actor's identity or permissions
  23. // before making a new request. It is typically an [ipnauth.TestActor],
  24. // unless the [Client] was created with s specific actor using [Server.ClientFor].
  25. Actor ipnauth.Actor
  26. }
  27. // Username returns username of the client's owner.
  28. func (c *Client) Username() string {
  29. c.tb.Helper()
  30. name, err := c.Actor.Username()
  31. if err != nil {
  32. c.tb.Fatalf("Client.Username: %v", err)
  33. }
  34. return name
  35. }
  36. // WatchIPNBus is like [local.Client.WatchIPNBus] but returns a [local.IPNBusWatcher]
  37. // that is closed when the test ends and a cancel function that stops the watcher.
  38. // It fails the test if the underlying WatchIPNBus returns an error.
  39. func (c *Client) WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (*local.IPNBusWatcher, context.CancelFunc) {
  40. c.tb.Helper()
  41. ctx, cancelWatcher := context.WithCancel(ctx)
  42. c.tb.Cleanup(cancelWatcher)
  43. watcher, err := c.Client.WatchIPNBus(ctx, mask)
  44. name, _ := c.Actor.Username()
  45. if err != nil {
  46. c.tb.Fatalf("Client.WatchIPNBus(%q): %v", name, err)
  47. }
  48. c.tb.Cleanup(func() { watcher.Close() })
  49. return watcher, cancelWatcher
  50. }
  51. // generateSequentialName generates a unique sequential name based on the given prefix and number n.
  52. // It uses a base-26 encoding to create names like "User-A", "User-B", ..., "User-Z", "User-AA", etc.
  53. func generateSequentialName(prefix string, n int) string {
  54. n++
  55. name := ""
  56. const numLetters = 'Z' - 'A' + 1
  57. for n > 0 {
  58. n--
  59. remainder := byte(n % numLetters)
  60. name = string([]byte{'A' + remainder}) + name
  61. n = n / numLetters
  62. }
  63. return prefix + "-" + name
  64. }