AtlatestRepositorysigil-youtube
sigil-youtube / tree / testyoutube-test.sgl
1
;;; Tests for (youtube) — core module2
;;;3
;;; Tests record construction, JSON response parsing, URL building,4
;;; and query string encoding using fixture data (no real API calls).6
(import (sigil core)7
(sigil dict)8
(sigil string)9
(sigil struct)10
(sigil json)11
(sigil test)12
(only (sigil http) url-encode-value build-query-string)13
(youtube))15
;; ---------------------------------------------------------------16
;; Test fixtures — JSON response samples17
;; ---------------------------------------------------------------19
(define video-resource-json20
(json-decode21
(string-append22
"{\"id\": \"dQw4w9WgXcQ\","23
" \"snippet\": {"24
" \"title\": \"Test Video\","25
" \"description\": \"A test description\","26
" \"tags\": [\"test\", \"sigil\", \"youtube\"],"27
" \"categoryId\": \"28\","28
" \"publishedAt\": \"2026-03-20T12:00:00Z\""29
" },"30
" \"status\": {"31
" \"privacyStatus\": \"public\""32
" },"33
" \"statistics\": {"34
" \"viewCount\": \"1234\","35
" \"likeCount\": \"56\","36
" \"commentCount\": \"7\""37
" }}")))39
(define video-no-tags-json40
(json-decode41
(string-append42
"{\"id\": \"abc123\","43
" \"snippet\": {"44
" \"title\": \"No Tags Video\","45
" \"description\": \"\""46
" },"47
" \"status\": {},"48
" \"statistics\": {}}")))50
(define channel-resource-json51
(json-decode52
(string-append53
"{\"id\": \"UCxxxxxxxxxxxxxx\","54
" \"snippet\": {"55
" \"title\": \"System Crafters\""56
" },"57
" \"statistics\": {"58
" \"subscriberCount\": \"50000\","59
" \"videoCount\": \"300\""60
" },"61
" \"contentDetails\": {"62
" \"relatedPlaylists\": {"63
" \"uploads\": \"UUxxxxxxxxxxxxxx\""64
" }"65
" }}")))67
(define channel-minimal-json68
(json-decode69
(string-append70
"{\"id\": \"UCyyyy\","71
" \"snippet\": {},"72
" \"statistics\": {},"73
" \"contentDetails\": {}}")))75
;; ---------------------------------------------------------------76
;; Client construction tests77
;; ---------------------------------------------------------------79
(test-group "client construction"81
(test "client with access token"82
(let ((c (youtube-client access-token: "ya29.test-token")))83
(assert-true (youtube-client? c))84
(assert-equal "ya29.test-token" (youtube-client-access-token c))85
(assert-equal #f (youtube-client-api-key c))86
(assert-equal "https://www.googleapis.com/youtube/v3"87
(youtube-client-base-url c))))89
(test "client with api key only"90
(let ((c (youtube-client api-key: "AIzaTest123")))91
(assert-equal #f (youtube-client-access-token c))92
(assert-equal "AIzaTest123" (youtube-client-api-key c))))94
(test "client with both tokens"95
(let ((c (youtube-client access-token: "ya29.tok"96
api-key: "AIza.key")))97
(assert-equal "ya29.tok" (youtube-client-access-token c))98
(assert-equal "AIza.key" (youtube-client-api-key c))))100
(test "custom base url"101
(let ((c (youtube-client access-token: "tok"102
base-url: "http://localhost:8080")))103
(assert-equal "http://localhost:8080"104
(youtube-client-base-url c)))))106
;; ---------------------------------------------------------------107
;; Video record tests108
;; ---------------------------------------------------------------110
(test-group "video records"112
(test "video record construction"113
(let ((v (youtube-video id: "vid1" title: "My Video"114
description: "desc" tags: '("a" "b")115
category-id: "22"116
privacy-status: "unlisted")))117
(assert-true (youtube-video? v))118
(assert-equal "vid1" (youtube-video-id v))119
(assert-equal "My Video" (youtube-video-title v))120
(assert-equal "desc" (youtube-video-description v))121
(assert-equal '("a" "b") (youtube-video-tags v))122
(assert-equal "22" (youtube-video-category-id v))123
(assert-equal "unlisted" (youtube-video-privacy-status v))))125
(test "video record defaults"126
(let ((v (youtube-video id: "vid2")))127
(assert-equal "" (youtube-video-title v))128
(assert-equal "" (youtube-video-description v))129
(assert-equal '() (youtube-video-tags v))130
(assert-equal #f (youtube-video-category-id v))131
(assert-equal "private" (youtube-video-privacy-status v))132
(assert-equal #f (youtube-video-published-at v)))))134
;; ---------------------------------------------------------------135
;; Channel record tests136
;; ---------------------------------------------------------------138
(test-group "channel records"140
(test "channel record construction"141
(let ((ch (youtube-channel id: "UC123" title: "My Channel"142
subscriber-count: 1000143
video-count: 50144
uploads-playlist-id: "UU123")))145
(assert-true (youtube-channel? ch))146
(assert-equal "UC123" (youtube-channel-id ch))147
(assert-equal "My Channel" (youtube-channel-title ch))148
(assert-equal 1000 (youtube-channel-subscriber-count ch))149
(assert-equal 50 (youtube-channel-video-count ch))150
(assert-equal "UU123" (youtube-channel-uploads-playlist-id ch))))152
(test "channel record defaults"153
(let ((ch (youtube-channel id: "UC456")))154
(assert-equal "" (youtube-channel-title ch))155
(assert-equal 0 (youtube-channel-subscriber-count ch))156
(assert-equal 0 (youtube-channel-video-count ch))157
(assert-equal #f (youtube-channel-uploads-playlist-id ch)))))159
;; ---------------------------------------------------------------160
;; Video parsing tests161
;; ---------------------------------------------------------------163
(test-group "video parsing"165
(test "parse full video resource"166
(let ((v (parse-video video-resource-json)))167
(assert-true (youtube-video? v))168
(assert-equal "dQw4w9WgXcQ" (youtube-video-id v))169
(assert-equal "Test Video" (youtube-video-title v))170
(assert-equal "A test description" (youtube-video-description v))171
(assert-equal '("test" "sigil" "youtube") (youtube-video-tags v))172
(assert-equal "28" (youtube-video-category-id v))173
(assert-equal "public" (youtube-video-privacy-status v))174
(assert-equal "2026-03-20T12:00:00Z" (youtube-video-published-at v))))176
(test "parse video statistics"177
(let ((v (parse-video video-resource-json)))178
(let ((stats (youtube-video-statistics v)))179
(assert-true (dict? stats))180
(assert-equal "1234" (dict-ref stats viewCount:))181
(assert-equal "56" (dict-ref stats likeCount:)))))183
(test "parse video with missing tags"184
(let ((v (parse-video video-no-tags-json)))185
(assert-equal "abc123" (youtube-video-id v))186
(assert-equal "No Tags Video" (youtube-video-title v))187
(assert-equal '() (youtube-video-tags v)))))189
;; ---------------------------------------------------------------190
;; Channel parsing tests191
;; ---------------------------------------------------------------193
(test-group "channel parsing"195
(test "parse full channel resource"196
(let ((ch (parse-channel channel-resource-json)))197
(assert-true (youtube-channel? ch))198
(assert-equal "UCxxxxxxxxxxxxxx" (youtube-channel-id ch))199
(assert-equal "System Crafters" (youtube-channel-title ch))200
(assert-equal 50000 (youtube-channel-subscriber-count ch))201
(assert-equal 300 (youtube-channel-video-count ch))202
(assert-equal "UUxxxxxxxxxxxxxx"203
(youtube-channel-uploads-playlist-id ch))))205
(test "parse minimal channel resource"206
(let ((ch (parse-channel channel-minimal-json)))207
(assert-equal "UCyyyy" (youtube-channel-id ch))208
(assert-equal "" (youtube-channel-title ch))209
(assert-equal 0 (youtube-channel-subscriber-count ch))210
(assert-equal 0 (youtube-channel-video-count ch))211
(assert-equal #f (youtube-channel-uploads-playlist-id ch)))))213
;; ---------------------------------------------------------------214
;; URL encoding tests215
;; ---------------------------------------------------------------217
(test-group "URL encoding"219
(test "encode simple string"220
(assert-equal "hello" (url-encode-value "hello")))222
(test "encode spaces"223
(assert-equal "hello+world" (url-encode-value "hello world")))225
(test "encode special characters"226
(assert-equal "foo%26bar" (url-encode-value "foo&bar")))228
(test "encode equals sign"229
(assert-equal "key%3Dvalue" (url-encode-value "key=value")))231
(test "preserve unreserved characters"232
(assert-equal "a-b_c.d~e" (url-encode-value "a-b_c.d~e"))))234
;; ---------------------------------------------------------------235
;; Query string building tests236
;; ---------------------------------------------------------------238
(test-group "query string building"240
(test "build simple query string"241
(assert-equal "?part=snippet&id=abc123"242
(build-query-string243
(list (cons "part" "snippet")244
(cons "id" "abc123")))))246
(test "omit false values"247
(assert-equal "?part=snippet"248
(build-query-string249
(list (cons "part" "snippet")250
(cons "pageToken" #f)))))252
(test "empty params"253
(assert-equal ""254
(build-query-string '())))256
(test "encode values in query string"257
(assert-equal "?q=hello+world"258
(build-query-string259
(list (cons "q" "hello world"))))))261
;; ---------------------------------------------------------------262
;; Auth headers tests263
;; ---------------------------------------------------------------265
(test-group "auth headers"267
(test "bearer token header"268
(let* ((c (youtube-client access-token: "ya29.test"))269
(headers (youtube-auth-headers c)))270
(assert-equal "Bearer ya29.test"271
(dict-ref headers authorization:))))273
(test "no header without token"274
(let* ((c (youtube-client api-key: "AIza.key"))275
(headers (youtube-auth-headers c)))276
(assert-equal #f (dict-ref headers authorization: #f)))))278
;; ---------------------------------------------------------------279
;; Page info parsing tests280
;; ---------------------------------------------------------------282
(test-group "page info parsing"284
(test "parse page info with next token"285
(let ((data #{ pageInfo: #{ totalResults: 42 }286
nextPageToken: "CDIQAA" }))287
(let ((info (parse-page-info data)))288
(assert-equal 42 (dict-ref info total-results:))289
(assert-equal "CDIQAA" (dict-ref info next-page-token:)))))291
(test "parse page info without next token"292
(let ((data #{ pageInfo: #{ totalResults: 5 } }))293
(let ((info (parse-page-info data)))294
(assert-equal 5 (dict-ref info total-results:))295
(assert-equal #f (dict-ref info next-page-token:))))))297
(run-tests)