Skip to content

Commit 184124b

Browse files
committed
update start time
1 parent eccbb4b commit 184124b

3 files changed

Lines changed: 195 additions & 0 deletions

File tree

cmd/sim/auth.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package sim
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"strings"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/duneanalytics/cli/authconfig"
12+
)
13+
14+
// NewAuthCmd returns the `sim auth` command.
15+
func NewAuthCmd() *cobra.Command {
16+
cmd := &cobra.Command{
17+
Use: "auth",
18+
Short: "Authenticate with the Sim API",
19+
Long: "Save your Sim API key so you don't need to pass --sim-api-key or set DUNE_SIM_API_KEY every time.",
20+
Annotations: map[string]string{"skipSimAuth": "true"},
21+
RunE: runSimAuth,
22+
}
23+
24+
cmd.Flags().String("api-key", "", "Sim API key to save")
25+
26+
return cmd
27+
}
28+
29+
func runSimAuth(cmd *cobra.Command, _ []string) error {
30+
key, _ := cmd.Flags().GetString("api-key")
31+
32+
if key == "" {
33+
key = os.Getenv("DUNE_SIM_API_KEY")
34+
}
35+
36+
if key == "" {
37+
fmt.Fprint(cmd.ErrOrStderr(), "Enter your Sim API key: ")
38+
scanner := bufio.NewScanner(cmd.InOrStdin())
39+
if scanner.Scan() {
40+
key = strings.TrimSpace(scanner.Text())
41+
}
42+
}
43+
44+
if key == "" {
45+
return fmt.Errorf("no API key provided")
46+
}
47+
48+
cfg, err := authconfig.Load()
49+
if err != nil {
50+
return fmt.Errorf("loading existing config: %w", err)
51+
}
52+
if cfg == nil {
53+
cfg = &authconfig.Config{}
54+
}
55+
cfg.SimAPIKey = key
56+
if err := authconfig.Save(cfg); err != nil {
57+
return fmt.Errorf("saving config: %w", err)
58+
}
59+
60+
p, _ := authconfig.Path()
61+
fmt.Fprintf(cmd.OutOrStdout(), "Sim API key saved to %s\n", p)
62+
return nil
63+
}

cmd/sim/auth_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package sim_test
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
11+
"github.com/duneanalytics/cli/authconfig"
12+
"github.com/duneanalytics/cli/cmd/sim"
13+
"github.com/spf13/cobra"
14+
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
16+
"gopkg.in/yaml.v3"
17+
)
18+
19+
func setupAuthTest(t *testing.T) string {
20+
t.Helper()
21+
dir := t.TempDir()
22+
authconfig.SetDirFunc(func() (string, error) { return dir, nil })
23+
t.Cleanup(authconfig.ResetDirFunc)
24+
// Clear env var so it doesn't interfere with tests.
25+
t.Setenv("DUNE_SIM_API_KEY", "")
26+
return dir
27+
}
28+
29+
func newSimAuthRoot() *cobra.Command {
30+
root := &cobra.Command{Use: "dune"}
31+
root.SetContext(context.Background())
32+
33+
simCmd := sim.NewSimCmd()
34+
root.AddCommand(simCmd)
35+
36+
return root
37+
}
38+
39+
func TestSimAuth_WithFlag(t *testing.T) {
40+
dir := setupAuthTest(t)
41+
42+
root := newSimAuthRoot()
43+
var buf bytes.Buffer
44+
root.SetOut(&buf)
45+
root.SetArgs([]string{"sim", "auth", "--api-key", "sk_sim_flag_key"})
46+
require.NoError(t, root.Execute())
47+
48+
data, err := os.ReadFile(filepath.Join(dir, "config.yaml"))
49+
require.NoError(t, err)
50+
assert.Contains(t, string(data), "sk_sim_flag_key")
51+
assert.Contains(t, buf.String(), "Sim API key saved to")
52+
}
53+
54+
func TestSimAuth_WithEnvVar(t *testing.T) {
55+
dir := setupAuthTest(t)
56+
57+
t.Setenv("DUNE_SIM_API_KEY", "sk_sim_env_key")
58+
59+
root := newSimAuthRoot()
60+
var buf bytes.Buffer
61+
root.SetOut(&buf)
62+
root.SetArgs([]string{"sim", "auth"})
63+
require.NoError(t, root.Execute())
64+
65+
data, err := os.ReadFile(filepath.Join(dir, "config.yaml"))
66+
require.NoError(t, err)
67+
assert.Contains(t, string(data), "sk_sim_env_key")
68+
}
69+
70+
func TestSimAuth_WithPrompt(t *testing.T) {
71+
dir := setupAuthTest(t)
72+
73+
root := newSimAuthRoot()
74+
var buf bytes.Buffer
75+
root.SetOut(&buf)
76+
root.SetIn(strings.NewReader("sk_sim_prompt_key\n"))
77+
root.SetArgs([]string{"sim", "auth"})
78+
require.NoError(t, root.Execute())
79+
80+
data, err := os.ReadFile(filepath.Join(dir, "config.yaml"))
81+
require.NoError(t, err)
82+
assert.Contains(t, string(data), "sk_sim_prompt_key")
83+
}
84+
85+
func TestSimAuth_EmptyInput(t *testing.T) {
86+
setupAuthTest(t)
87+
88+
root := newSimAuthRoot()
89+
root.SetIn(strings.NewReader("\n"))
90+
root.SetArgs([]string{"sim", "auth"})
91+
err := root.Execute()
92+
assert.Error(t, err)
93+
assert.Contains(t, err.Error(), "no API key provided")
94+
}
95+
96+
func TestSimAuth_PreservesExistingConfig(t *testing.T) {
97+
dir := setupAuthTest(t)
98+
99+
// Pre-populate config with existing fields.
100+
existing := &authconfig.Config{
101+
APIKey: "existing_dune_key",
102+
}
103+
telemetryTrue := true
104+
existing.Telemetry = &telemetryTrue
105+
require.NoError(t, authconfig.Save(existing))
106+
107+
root := newSimAuthRoot()
108+
var buf bytes.Buffer
109+
root.SetOut(&buf)
110+
root.SetArgs([]string{"sim", "auth", "--api-key", "sk_sim_new"})
111+
require.NoError(t, root.Execute())
112+
113+
// Verify all fields are preserved.
114+
data, err := os.ReadFile(filepath.Join(dir, "config.yaml"))
115+
require.NoError(t, err)
116+
117+
var cfg authconfig.Config
118+
require.NoError(t, yaml.Unmarshal(data, &cfg))
119+
120+
assert.Equal(t, "existing_dune_key", cfg.APIKey, "existing api_key should be preserved")
121+
assert.Equal(t, "sk_sim_new", cfg.SimAPIKey, "sim_api_key should be set")
122+
require.NotNil(t, cfg.Telemetry, "telemetry should be preserved")
123+
assert.True(t, *cfg.Telemetry, "telemetry value should be preserved")
124+
}

cmd/sim/sim.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"strings"
7+
"time"
78

89
"github.com/spf13/cobra"
910

@@ -33,6 +34,7 @@ func NewSimCmd() *cobra.Command {
3334
"Sim API key (overrides DUNE_SIM_API_KEY env var)",
3435
)
3536

37+
cmd.AddCommand(NewAuthCmd())
3638
cmd.AddCommand(evm.NewEvmCmd())
3739
cmd.AddCommand(svm.NewSvmCmd())
3840

@@ -42,6 +44,12 @@ func NewSimCmd() *cobra.Command {
4244
// simPreRun resolves the Sim API key and stores a SimClient in the command context.
4345
// Commands annotated with "skipSimAuth": "true" bypass this step.
4446
func simPreRun(cmd *cobra.Command, _ []string) error {
47+
// The sim command's PersistentPreRunE overrides the root command's hook
48+
// (cobra does not chain PersistentPreRunE without EnableTraverseRunHooks).
49+
// Record the start time here so the root's PersistentPostRunE computes a
50+
// correct duration for telemetry.
51+
cmdutil.SetStartTime(cmd, time.Now())
52+
4553
// Allow commands like `sim auth` to skip sim client creation.
4654
if cmd.Annotations["skipSimAuth"] == "true" {
4755
return nil

0 commit comments

Comments
 (0)