Skip to content

Commit 4045c24

Browse files
committed
tailscale: guard last_seen nil and add tests
Adds last_seen nil check to deviceToMap Adds unit tests covering new data_source device fields Signed-off-by: Zach Buchheit <zachb@tailscale.com>
1 parent 547ea2a commit 4045c24

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

tailscale/data_source_device.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@ func dataSourceDeviceRead(ctx context.Context, d *schema.ResourceData, m interfa
193193
// resource in Terraform. This omits the "id" which is expected to be set
194194
// using [schema.ResourceData.SetId].
195195
func deviceToMap(device *tailscale.Device) map[string]any {
196+
var lastSeen string
197+
if device.LastSeen == nil {
198+
lastSeen = ""
199+
} else {
200+
lastSeen = device.LastSeen.Format(time.RFC3339)
201+
}
202+
196203
return map[string]any{
197204
"name": device.Name,
198205
"hostname": device.Hostname,
@@ -207,7 +214,7 @@ func deviceToMap(device *tailscale.Device) map[string]any {
207214
"created": device.Created.Format(time.RFC3339),
208215
"expires": device.Expires.Format(time.RFC3339),
209216
"is_external": device.IsExternal,
210-
"last_seen": device.LastSeen.Format(time.RFC3339),
217+
"last_seen": lastSeen,
211218
"machine_key": device.MachineKey,
212219
"node_key": device.NodeKey,
213220
"os": device.OS,
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package tailscale
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/assert"
8+
tsclient "tailscale.com/client/tailscale/v2"
9+
"tailscale.com/tstest"
10+
)
11+
12+
func TestDeviceToMap(t *testing.T) {
13+
t.Parallel()
14+
cl := tstest.NewClock(tstest.ClockOpts{})
15+
created := tsclient.Time{Time: cl.Now().Truncate(time.Second)}
16+
expires := tsclient.Time{Time: cl.Now().Truncate(time.Second).Add(24 * time.Hour)}
17+
lastSeen := tsclient.Time{Time: cl.Now().Truncate(time.Second).Add(-5 * time.Minute)}
18+
19+
dev := &tsclient.Device{
20+
Name: "host.example.ts.net",
21+
Hostname: "host",
22+
User: "user@example.com",
23+
NodeID: "node-123",
24+
Addresses: []string{"100.100.100.101", "fd7a:115c:a1e0::1"},
25+
Tags: []string{"tag:test1", "tag:test2"},
26+
Authorized: true,
27+
KeyExpiryDisabled: true,
28+
BlocksIncomingConnections: true,
29+
ClientVersion: "1.88.4",
30+
Created: created,
31+
Expires: expires,
32+
IsExternal: false,
33+
LastSeen: &lastSeen,
34+
MachineKey: "machine-key",
35+
NodeKey: "node-key",
36+
OS: "linux",
37+
UpdateAvailable: true,
38+
TailnetLockError: "lock-error",
39+
TailnetLockKey: "lock-key",
40+
}
41+
42+
m := deviceToMap(dev)
43+
44+
assert.Equal(t, dev.Name, m["name"].(string))
45+
assert.Equal(t, dev.Hostname, m["hostname"].(string))
46+
assert.Equal(t, dev.User, m["user"].(string))
47+
assert.Equal(t, dev.NodeID, m["node_id"].(string))
48+
assert.Equal(t, dev.Addresses, m["addresses"].([]string))
49+
assert.Equal(t, dev.Tags, m["tags"].([]string))
50+
assert.Equal(t, dev.Authorized, m["authorized"].(bool))
51+
assert.Equal(t, dev.KeyExpiryDisabled, m["key_expiry_disabled"].(bool))
52+
assert.Equal(t, dev.BlocksIncomingConnections, m["blocks_incoming_connections"].(bool))
53+
assert.Equal(t, dev.ClientVersion, m["client_version"].(string))
54+
assert.Equal(t, created.Format(time.RFC3339), m["created"].(string))
55+
assert.Equal(t, expires.Format(time.RFC3339), m["expires"].(string))
56+
assert.Equal(t, dev.IsExternal, m["is_external"].(bool))
57+
assert.Equal(t, lastSeen.Format(time.RFC3339), m["last_seen"].(string))
58+
assert.Equal(t, dev.MachineKey, m["machine_key"].(string))
59+
assert.Equal(t, dev.NodeKey, m["node_key"].(string))
60+
assert.Equal(t, dev.OS, m["os"].(string))
61+
assert.Equal(t, dev.UpdateAvailable, m["update_available"].(bool))
62+
assert.Equal(t, dev.TailnetLockError, m["tailnet_lock_error"].(string))
63+
assert.Equal(t, dev.TailnetLockKey, m["tailnet_lock_key"].(string))
64+
}
65+
func TestDeviceToMap_LastSeenNil(t *testing.T) {
66+
t.Parallel()
67+
cl := tstest.NewClock(tstest.ClockOpts{})
68+
created := tsclient.Time{Time: cl.Now().Truncate(time.Second)}
69+
expires := tsclient.Time{Time: cl.Now().Truncate(time.Second).Add(24 * time.Hour)}
70+
71+
dev := &tsclient.Device{
72+
Name: "host.example.ts.net",
73+
Hostname: "host",
74+
User: "user@example.com",
75+
NodeID: "node-123",
76+
Addresses: []string{"100.100.100.101", "fd7a:115c:a1e0::1"},
77+
Tags: []string{"tag:test1", "tag:test2"},
78+
Authorized: true,
79+
KeyExpiryDisabled: true,
80+
BlocksIncomingConnections: true,
81+
ClientVersion: "1.88.4",
82+
Created: created,
83+
Expires: expires,
84+
IsExternal: false,
85+
LastSeen: nil,
86+
MachineKey: "machine-key",
87+
NodeKey: "node-key",
88+
OS: "linux",
89+
UpdateAvailable: true,
90+
TailnetLockError: "lock-error",
91+
TailnetLockKey: "lock-key",
92+
}
93+
94+
m := deviceToMap(dev)
95+
96+
assert.Equal(t, dev.Name, m["name"].(string))
97+
assert.Equal(t, dev.Hostname, m["hostname"].(string))
98+
assert.Equal(t, dev.User, m["user"].(string))
99+
assert.Equal(t, dev.NodeID, m["node_id"].(string))
100+
assert.Equal(t, dev.Addresses, m["addresses"].([]string))
101+
assert.Equal(t, dev.Tags, m["tags"].([]string))
102+
assert.Equal(t, dev.Authorized, m["authorized"].(bool))
103+
assert.Equal(t, dev.KeyExpiryDisabled, m["key_expiry_disabled"].(bool))
104+
assert.Equal(t, dev.BlocksIncomingConnections, m["blocks_incoming_connections"].(bool))
105+
assert.Equal(t, dev.ClientVersion, m["client_version"].(string))
106+
assert.Equal(t, created.Format(time.RFC3339), m["created"].(string))
107+
assert.Equal(t, expires.Format(time.RFC3339), m["expires"].(string))
108+
assert.Equal(t, dev.IsExternal, m["is_external"].(bool))
109+
assert.Equal(t, "", m["last_seen"]) // Expect empty string for nil LastSeen
110+
assert.Equal(t, dev.MachineKey, m["machine_key"].(string))
111+
assert.Equal(t, dev.NodeKey, m["node_key"].(string))
112+
assert.Equal(t, dev.OS, m["os"].(string))
113+
assert.Equal(t, dev.UpdateAvailable, m["update_available"].(bool))
114+
assert.Equal(t, dev.TailnetLockError, m["tailnet_lock_error"].(string))
115+
assert.Equal(t, dev.TailnetLockKey, m["tailnet_lock_key"].(string))
116+
}

0 commit comments

Comments
 (0)