mirror of
https://github.com/coder/coder.git
synced 2026-11-03 14:21:04 +08:00
Phase 1 of the Scalable Approach for Adding New IDE Session Types RFC, tracked in #27410. Gives agent session accounting one canonical vocabulary for app names, so later work can report a count per app without the agent and the server disagreeing about what an app is called. - Adds codersdk/appname.go: NormalizeAppName for storage and lookup, and AppFamilyName for the places that need a bounded value, such as a Prometheus label or the Connection_Type enum. - agentssh.Server keeps session counters in a map keyed by normalized name instead of three fixed atomics, dropping entries at zero. - Drops MagicSessionType. Sessions carry plain app names, and unrecognized ones flow through as themselves rather than collapsing to unknown. Wire behavior is unchanged: the agent still reports the four session_count_* fields over Agent API 2.10.
89 lines
2.1 KiB
Go
89 lines
2.1 KiB
Go
package agentssh
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestExtractAppName(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
envWith := func(value string) []string {
|
|
return []string{
|
|
"FOO=bar",
|
|
fmt.Sprintf("%s=%s", AppNameEnvironmentVariable, value),
|
|
"BAZ=qux",
|
|
}
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
env []string
|
|
want string
|
|
}{
|
|
{"NoEnvDefaultsToSSH", []string{"FOO=bar"}, "ssh"},
|
|
{"EmptyValueDefaultsToSSH", envWith(""), "ssh"},
|
|
{"VSCode", envWith("vscode"), "vscode"},
|
|
{"JetBrainsLegacyCasing", envWith("JetBrains"), "jetbrains"},
|
|
{"UnknownTypeNormalized", envWith("Cursor-Nightly"), "cursor_nightly"},
|
|
{"LastInstanceWins", append(envWith("vscode"), AppNameEnvironmentVariable+"=cursor"), "cursor"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
appName, _, filteredEnv := extractAppName(tc.env)
|
|
require.Equal(t, tc.want, appName)
|
|
for _, kv := range filteredEnv {
|
|
require.NotContains(t, kv, AppNameEnvironmentVariable+"=")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAppNameMetricLabel(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, tc := range []struct {
|
|
name string
|
|
appName string
|
|
want string
|
|
}{
|
|
{"SSH", "ssh", "ssh"},
|
|
{"ForkUsesFamily", "cursor", "vscode"},
|
|
// No alias for the nightly variant, so it does not get a label.
|
|
{"UnlistedVariant", "cursor_nightly", "unknown"},
|
|
{"Unknown", "some_new_ide", "unknown"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
require.Equal(t, tc.want, appNameMetricLabel(tc.appName))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSessionCounts(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
s := &Server{sessionCounts: make(map[string]int64)}
|
|
|
|
// Counters are created on demand and names are normalized.
|
|
_ = s.startSession("ssh")
|
|
_ = s.startSession("vscode")
|
|
endCursor1 := s.startSession("Cursor")
|
|
endCursor2 := s.startSession("cursor")
|
|
require.Equal(t, map[string]int64{
|
|
"ssh": 1,
|
|
"vscode": 1,
|
|
"cursor": 2,
|
|
}, s.SessionCounts())
|
|
|
|
// Zero-count entries are dropped, not reported as idle apps.
|
|
endCursor1()
|
|
endCursor2()
|
|
require.Equal(t, map[string]int64{
|
|
"ssh": 1,
|
|
"vscode": 1,
|
|
}, s.SessionCounts())
|
|
}
|