feat: add onboarding info fields to first user setup (#23989)

Add optional demographic and newsletter preference fields to the setup
page: business use (yes/no), industry type, organization size, and two
newsletter toggles (marketing, release/security updates).

The new data flows through telemetry via a FirstUserOnboarding struct in
the snapshot payload, sent once when the first user is created. The
telemetry-server and BigQuery schema changes are required separately to
persist this data.

---------

Co-authored-by: default <davidiii@fraley.us>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeremy Ruppel
2026-04-03 09:52:52 -04:00
committed by GitHub
co-authored by default Claude Sonnet 4.6
parent 53482adc2d
commit da3c46b557
9 changed files with 549 additions and 15 deletions
+87
View File
@@ -116,6 +116,93 @@ func TestFirstUser(t *testing.T) {
})
}
func TestFirstUser_OnboardingTelemetry(t *testing.T) {
t.Parallel()
t.Run("OnboardingInfoFlowsToSnapshot", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitMedium)
fTelemetry := newFakeTelemetryReporter(ctx, t, 10)
client := coderdtest.New(t, &coderdtest.Options{
TelemetryReporter: fTelemetry,
})
isBusiness := true
wantMarketing := false
wantReleases := true
_, err := client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "admin@coder.com",
Username: "admin",
Password: "SomeSecurePassword!",
OnboardingInfo: &codersdk.CreateFirstUserOnboardingInfo{
IsBusiness: &isBusiness,
IndustryType: codersdk.IndustryTypeTechnology,
OrgSize: codersdk.OrgSizeRange51To200,
NewsletterMarketing: &wantMarketing,
NewsletterReleases: &wantReleases,
},
})
require.NoError(t, err)
snapshot := testutil.TryReceive(ctx, t, fTelemetry.snapshots)
require.NotNil(t, snapshot.FirstUserOnboarding)
require.NotNil(t, snapshot.FirstUserOnboarding.IsBusiness)
require.True(t, *snapshot.FirstUserOnboarding.IsBusiness)
require.Equal(t, string(codersdk.IndustryTypeTechnology), snapshot.FirstUserOnboarding.IndustryType)
require.Equal(t, string(codersdk.OrgSizeRange51To200), snapshot.FirstUserOnboarding.OrgSize)
require.NotNil(t, snapshot.FirstUserOnboarding.NewsletterMarketing)
require.False(t, *snapshot.FirstUserOnboarding.NewsletterMarketing)
require.NotNil(t, snapshot.FirstUserOnboarding.NewsletterReleases)
require.True(t, *snapshot.FirstUserOnboarding.NewsletterReleases)
})
t.Run("NilWhenOnboardingInfoOmitted", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitMedium)
fTelemetry := newFakeTelemetryReporter(ctx, t, 10)
client := coderdtest.New(t, &coderdtest.Options{
TelemetryReporter: fTelemetry,
})
_, err := client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "admin@coder.com",
Username: "admin",
Password: "SomeSecurePassword!",
// No OnboardingInfo — simulates old CLI or OIDC flow.
})
require.NoError(t, err)
snapshot := testutil.TryReceive(ctx, t, fTelemetry.snapshots)
require.Nil(t, snapshot.FirstUserOnboarding)
})
t.Run("EmptyOnboardingInfoIsNonNilWithZeroFields", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitMedium)
fTelemetry := newFakeTelemetryReporter(ctx, t, 10)
client := coderdtest.New(t, &coderdtest.Options{
TelemetryReporter: fTelemetry,
})
_, err := client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{
Email: "admin@coder.com", Username: "admin",
Password: "SomeSecurePassword!",
OnboardingInfo: &codersdk.CreateFirstUserOnboardingInfo{},
})
require.NoError(t, err)
snapshot := testutil.TryReceive(ctx, t, fTelemetry.snapshots)
require.NotNil(t, snapshot.FirstUserOnboarding,
"non-nil OnboardingInfo must produce non-nil telemetry")
require.Nil(t, snapshot.FirstUserOnboarding.IsBusiness,
"nil *bool must stay nil, not become false")
require.Nil(t, snapshot.FirstUserOnboarding.NewsletterMarketing)
require.Nil(t, snapshot.FirstUserOnboarding.NewsletterReleases)
require.Empty(t, snapshot.FirstUserOnboarding.IndustryType)
require.Empty(t, snapshot.FirstUserOnboarding.OrgSize)
})
}
func TestPostLogin(t *testing.T) {
t.Parallel()
t.Run("InvalidUser", func(t *testing.T) {