Skip to content
Open
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
483 changes: 483 additions & 0 deletions internal/cli/glovo_cmd.go

Large diffs are not rendered by default.

162 changes: 162 additions & 0 deletions internal/cli/glovo_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package cli

import (
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
)

func TestGlovoCLI_ConfigSetAndShow(t *testing.T) {
cfgPath := filepath.Join(t.TempDir(), "config.json")

// Test config set
out, _, err := runCLI(cfgPath, []string{"glovo", "config", "set",
"--city-code", "MAD",
"--country-code", "ES",
"--language", "en",
}, "")
if err != nil {
t.Fatalf("config set: %v out=%s", err, out)
}

// Test config show
out, _, err = runCLI(cfgPath, []string{"glovo", "config", "show"}, "")
if err != nil {
t.Fatalf("config show: %v", err)
}
if !strings.Contains(out, "city_code=MAD") {
t.Fatalf("missing city_code in output: %s", out)
}
if !strings.Contains(out, "country_code=ES") {
t.Fatalf("missing country_code in output: %s", out)
}
if !strings.Contains(out, "language=en") {
t.Fatalf("missing language in output: %s", out)
}
}

func TestGlovoCLI_SessionCommand(t *testing.T) {
cfgPath := filepath.Join(t.TempDir(), "config.json")

// Set access token
out, _, err := runCLI(cfgPath, []string{"glovo", "session", "test-token-12345"}, "")
if err != nil {
t.Fatalf("session: %v out=%s", err, out)
}
if !strings.Contains(out, "access token saved") {
t.Fatalf("unexpected output: %s", out)
}

// Verify token is visible in config show (truncated with ...)
out, _, err = runCLI(cfgPath, []string{"glovo", "config", "show"}, "")
if err != nil {
t.Fatalf("config show: %v", err)
}
if !strings.Contains(out, "access_token=test-token-12345...") {
t.Fatalf("token not visible in config: %s", out)
}
}

func TestGlovoCLI_History(t *testing.T) {
cfgPath := filepath.Join(t.TempDir(), "config.json")

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check path
if !strings.HasPrefix(r.URL.Path, "/v3/customer/orders-list") {
t.Errorf("unexpected path: %s", r.URL.Path)
}
// Return mock order response
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{
"pagination": {"currentLimit": 12, "next": null},
"orders": [{
"orderId": 123,
"orderUrn": "glv:order:test",
"content": {"title": "Test Restaurant", "body": [{"type": "TEXT", "data": "1 x Item"}]},
"footer": {"left": {"type": "TEXT", "data": "10,00 EUR"}, "right": null},
"style": "DEFAULT",
"layoutType": "INACTIVE_ORDER",
"image": {"lightImageId": "", "darkImageId": ""}
}]
}`))
}))
defer srv.Close()

// Set config with test server URL and token
_, _, err := runCLI(cfgPath, []string{"glovo", "config", "set", "--base-url", srv.URL}, "")
if err != nil {
t.Fatalf("config set: %v", err)
}
_, _, err = runCLI(cfgPath, []string{"glovo", "session", "test-token"}, "")
if err != nil {
t.Fatalf("session: %v", err)
}

// Test history command
out, _, err := runCLI(cfgPath, []string{"glovo", "history"}, "")
if err != nil {
t.Fatalf("history: %v out=%s", err, out)
}
if !strings.Contains(out, "Test Restaurant") {
t.Fatalf("restaurant not found in output: %s", out)
}
if !strings.Contains(out, "10,00 EUR") {
t.Fatalf("price not found in output: %s", out)
}
}

func TestGlovoCLI_Me(t *testing.T) {
cfgPath := filepath.Join(t.TempDir(), "config.json")

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v3/me" {
t.Errorf("unexpected path: %s", r.URL.Path)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{
"id": 12345,
"type": "Customer",
"urn": "glv:customer:test",
"name": "Test User",
"email": "test@example.com",
"preferredCityCode": "MAD",
"preferredLanguage": "en",
"deliveredOrdersCount": 5
}`))
}))
defer srv.Close()

// Set config
_, _, err := runCLI(cfgPath, []string{"glovo", "config", "set", "--base-url", srv.URL}, "")
if err != nil {
t.Fatalf("config set: %v", err)
}
_, _, err = runCLI(cfgPath, []string{"glovo", "session", "test-token"}, "")
if err != nil {
t.Fatalf("session: %v", err)
}

// Test me command
out, _, err := runCLI(cfgPath, []string{"glovo", "me"}, "")
if err != nil {
t.Fatalf("me: %v out=%s", err, out)
}
if !strings.Contains(out, "Test User") {
t.Fatalf("name not found in output: %s", out)
}
if !strings.Contains(out, "test@example.com") {
t.Fatalf("email not found in output: %s", out)
}
}

func TestGlovoCLI_MissingToken(t *testing.T) {
cfgPath := filepath.Join(t.TempDir(), "config.json")

// Try to run history without token
_, _, err := runCLI(cfgPath, []string{"glovo", "history"}, "")
if err == nil {
t.Fatalf("expected error when token missing")
}
}
1 change: 1 addition & 0 deletions internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func newRoot() *cobra.Command {

cmd.AddCommand(newFoodoraCmd(st))
cmd.AddCommand(newDeliverooCmd(st))
cmd.AddCommand(newGlovoCmd(st))

return cmd
}
2 changes: 2 additions & 0 deletions internal/cli/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func (s *state) foodora() *config.FoodoraConfig { return s.cfg.Foodora() }

func (s *state) deliveroo() *config.DeliverooConfig { return s.cfg.Deliveroo() }

func (s *state) glovo() *config.GlovoConfig { return s.cfg.Glovo() }

func (s *state) load() error {
if s.configPath == "" {
p, err := config.DefaultPath()
Expand Down
21 changes: 21 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Config struct {
type Providers struct {
Foodora *FoodoraConfig `json:"foodora,omitempty"`
Deliveroo *DeliverooConfig `json:"deliveroo,omitempty"`
Glovo *GlovoConfig `json:"glovo,omitempty"`
}

type FoodoraConfig struct {
Expand Down Expand Up @@ -45,6 +46,17 @@ type DeliverooConfig struct {
BaseURL string `json:"base_url,omitempty"`
}

type GlovoConfig struct {
BaseURL string `json:"base_url,omitempty"`
AccessToken string `json:"access_token,omitempty"`
DeviceURN string `json:"device_urn,omitempty"`
CityCode string `json:"city_code,omitempty"`
CountryCode string `json:"country_code,omitempty"`
Language string `json:"language,omitempty"`
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
}

func DefaultPath() (string, error) {
dir, err := os.UserConfigDir()
if err != nil {
Expand Down Expand Up @@ -157,6 +169,15 @@ func (c *Config) Deliveroo() *DeliverooConfig {
return c.Providers.Deliveroo
}

func (c *Config) Glovo() *GlovoConfig {
if c.Providers.Glovo == nil {
c.Providers.Glovo = &GlovoConfig{
BaseURL: "https://api.glovoapp.com",
}
}
return c.Providers.Glovo
}

func (c FoodoraConfig) HasSession() bool {
return c.AccessToken != "" && c.RefreshToken != ""
}
Expand Down
Loading