Skip to content

Commit 520a1ca

Browse files
committed
Add tests
Signed-off-by: Danny Kopping <danny@coder.com>
1 parent 24b37d9 commit 520a1ca

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

coderd/database/dbmem/dbmem.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7680,6 +7680,7 @@ func (q *FakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl
76807680
AllowUserAutostart: true,
76817681
AllowUserAutostop: true,
76827682
MaxPortSharingLevel: arg.MaxPortSharingLevel,
7683+
CORSBehavior: arg.CORSBehavior,
76837684
}
76847685
q.templates = append(q.templates, template)
76857686
return nil
@@ -9213,6 +9214,7 @@ func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
92139214
tpl.GroupACL = arg.GroupACL
92149215
tpl.AllowUserCancelWorkspaceJobs = arg.AllowUserCancelWorkspaceJobs
92159216
tpl.MaxPortSharingLevel = arg.MaxPortSharingLevel
9217+
tpl.CORSBehavior = arg.CORSBehavior
92169218
q.templates[idx] = tpl
92179219
return nil
92189220
}

coderd/workspaceapps/apptest/apptest.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,151 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
14631463
require.Equal(t, http.StatusOK, resp.StatusCode)
14641464
assertWorkspaceLastUsedAtUpdated(t, appDetails)
14651465
})
1466+
1467+
t.Run("CORS", func(t *testing.T) {
1468+
t.Parallel()
1469+
1470+
// Set up test headers that should be returned by the app
1471+
testHeaders := http.Header{
1472+
"Access-Control-Allow-Origin": []string{"*"},
1473+
"Access-Control-Allow-Methods": []string{"GET, POST, OPTIONS"},
1474+
}
1475+
1476+
unauthenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
1477+
c := appDetails.AppClient(t)
1478+
c.SetSessionToken("")
1479+
return c
1480+
}
1481+
1482+
authenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
1483+
uc, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
1484+
c := appDetails.AppClient(t)
1485+
c.SetSessionToken(uc.SessionToken())
1486+
return c
1487+
}
1488+
1489+
ownerClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
1490+
return appDetails.SDKClient
1491+
}
1492+
1493+
tests := []struct {
1494+
name string
1495+
shareLevel codersdk.WorkspaceAgentPortShareLevel
1496+
behavior codersdk.AppCORSBehavior
1497+
client func(t *testing.T, appDetails *Details) *codersdk.Client
1498+
expectedStatusCode int
1499+
expectedCORSHeaders bool
1500+
}{
1501+
// Public
1502+
{
1503+
name: "Default/Public",
1504+
shareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
1505+
behavior: codersdk.AppCORSBehaviorSimple,
1506+
expectedCORSHeaders: false,
1507+
client: unauthenticatedClient,
1508+
expectedStatusCode: http.StatusOK,
1509+
},
1510+
{
1511+
name: "Passthru/Public",
1512+
shareLevel: codersdk.WorkspaceAgentPortShareLevelPublic,
1513+
behavior: codersdk.AppCORSBehaviorPassthru,
1514+
expectedCORSHeaders: true,
1515+
client: unauthenticatedClient,
1516+
expectedStatusCode: http.StatusOK,
1517+
},
1518+
// Authenticated
1519+
{
1520+
name: "Default/Authenticated",
1521+
shareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
1522+
behavior: codersdk.AppCORSBehaviorSimple,
1523+
expectedCORSHeaders: false,
1524+
client: authenticatedClient,
1525+
expectedStatusCode: http.StatusOK,
1526+
},
1527+
{
1528+
name: "Passthru/Authenticated",
1529+
shareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
1530+
behavior: codersdk.AppCORSBehaviorPassthru,
1531+
expectedCORSHeaders: true,
1532+
client: authenticatedClient,
1533+
expectedStatusCode: http.StatusOK,
1534+
},
1535+
{
1536+
// The CORS behavior will not affect unauthenticated requests.
1537+
// The request will be redirected to the login page.
1538+
name: "Passthru/Unauthenticated",
1539+
shareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated,
1540+
behavior: codersdk.AppCORSBehaviorPassthru,
1541+
expectedCORSHeaders: false,
1542+
client: unauthenticatedClient,
1543+
expectedStatusCode: http.StatusSeeOther,
1544+
},
1545+
// Owner
1546+
{
1547+
name: "Default/Owner",
1548+
shareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated, // Owner is not a valid share level for ports.
1549+
behavior: codersdk.AppCORSBehaviorSimple,
1550+
expectedCORSHeaders: false,
1551+
client: ownerClient,
1552+
expectedStatusCode: http.StatusOK,
1553+
},
1554+
{
1555+
name: "Passthru/Owner",
1556+
shareLevel: codersdk.WorkspaceAgentPortShareLevelAuthenticated, // Owner is not a valid share level for ports.
1557+
behavior: codersdk.AppCORSBehaviorPassthru,
1558+
expectedCORSHeaders: true,
1559+
client: ownerClient,
1560+
expectedStatusCode: http.StatusOK,
1561+
},
1562+
}
1563+
1564+
for _, tc := range tests {
1565+
t.Run(tc.name, func(t *testing.T) {
1566+
t.Parallel()
1567+
1568+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1569+
defer cancel()
1570+
1571+
appDetails := setupProxyTest(t, &DeploymentOptions{
1572+
headers: testHeaders,
1573+
})
1574+
port, err := strconv.ParseInt(appDetails.Apps.Port.AppSlugOrPort, 10, 32)
1575+
require.NoError(t, err)
1576+
1577+
// Update the template CORS behavior.
1578+
b := codersdk.AppCORSBehavior(tc.behavior)
1579+
template, err := appDetails.SDKClient.UpdateTemplateMeta(ctx, appDetails.Workspace.TemplateID, codersdk.UpdateTemplateMeta{
1580+
CORSBehavior: &b,
1581+
})
1582+
require.NoError(t, err)
1583+
require.Equal(t, tc.behavior, template.CORSBehavior)
1584+
1585+
// Set the port we have to be shared.
1586+
_, err = appDetails.SDKClient.UpsertWorkspaceAgentPortShare(ctx, appDetails.Workspace.ID, codersdk.UpsertWorkspaceAgentPortShareRequest{
1587+
AgentName: proxyTestAgentName,
1588+
Port: int32(port),
1589+
ShareLevel: tc.shareLevel,
1590+
Protocol: codersdk.WorkspaceAgentPortShareProtocolHTTP,
1591+
})
1592+
require.NoError(t, err)
1593+
1594+
client := tc.client(t, appDetails)
1595+
1596+
resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.Port).String(), nil)
1597+
require.NoError(t, err)
1598+
defer resp.Body.Close()
1599+
require.Equal(t, tc.expectedStatusCode, resp.StatusCode)
1600+
1601+
if tc.expectedCORSHeaders {
1602+
require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
1603+
require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
1604+
} else {
1605+
require.Empty(t, resp.Header.Get("Access-Control-Allow-Origin"))
1606+
require.Empty(t, resp.Header.Get("Access-Control-Allow-Methods"))
1607+
}
1608+
})
1609+
}
1610+
})
14661611
})
14671612

14681613
t.Run("AppSharing", func(t *testing.T) {

0 commit comments

Comments
 (0)