autograder.testing.model

Data for tests. Most model data will be copied over from the LMS toolkit and updated for the autograder.

  1"""
  2Data for tests.
  3Most model data will be copied over from the LMS toolkit and updated for the autograder.
  4"""
  5
  6import os
  7import typing
  8
  9import lms.model.assignments
 10import lms.model.testdata.users
 11import lms.model.users
 12import edq.util.json
 13import edq.util.time
 14
 15import autograder.model.course
 16import autograder.model.log
 17import autograder.testing.constants
 18
 19def _clean_server_user(user: lms.model.users.ServerUser) -> None:
 20    """
 21    Clean the server user for use with the autograder.
 22    Replace the ID with email.
 23    """
 24
 25    user.id = str(user.email)
 26
 27def _clean_server_users() -> typing.Dict[str, lms.model.users.ServerUser]:
 28    """ Copy over the LMS Toolkit test server users, but clean users. """
 29
 30    users = lms.model.testdata.users.SERVER_USERS.copy()
 31    for user in users.values():
 32        _clean_server_user(user)
 33
 34    return users
 35
 36def _clean_course_users() -> typing.Dict[str, typing.Dict[str, lms.model.users.CourseUser]]:
 37    """ Copy over the LMS Toolkit test course users, but clean users. """
 38
 39    course_users = {}
 40
 41    for name, raw_users in lms.model.testdata.users.COURSE_USERS.items():
 42        users = raw_users.copy()
 43        for user in users.values():
 44            _clean_server_user(user)
 45
 46        course_users[name] = users
 47
 48    return course_users
 49
 50# {name: user, ...}
 51SERVER_USERS: typing.Dict[str, lms.model.users.ServerUser] = _clean_server_users()
 52
 53# {course_name: {user_name: user, ...}, ...}
 54COURSE_USERS: typing.Dict[str, typing.Dict[str, lms.model.users.CourseUser]] = _clean_course_users()
 55
 56RAW_USER_DATA: typing.Dict[str, typing.Any] = {}
 57if (os.path.exists(autograder.testing.constants.SUBMODULE_SERVER_USERS_PATH)):
 58    RAW_USER_DATA = edq.util.json.load_path(autograder.testing.constants.SUBMODULE_SERVER_USERS_PATH)
 59
 60ASSIGNMENTS: typing.Dict[str, typing.Dict[str, lms.model.assignments.Assignment]] = {
 61    "Course 101": {
 62        'hw0': lms.model.assignments.Assignment(
 63            id = "hw0",
 64            name = "Homework 0",
 65        ),
 66    },
 67    "Course Using Different Languages": {
 68        'bash': lms.model.assignments.Assignment(
 69            id = "bash",
 70            name = "A Simple Bash Assignment",
 71            due_date = edq.util.time.Timestamp(0),
 72        ),
 73        'cpp': lms.model.assignments.Assignment(
 74            id = "cpp",
 75            name = "A Simple C++ Assignment",
 76        ),
 77        'java': lms.model.assignments.Assignment(
 78            id = "java",
 79            name = "A Simple Java Assignment",
 80        ),
 81    },
 82}
 83
 84COURSES: typing.Dict[str, autograder.model.course.Course] = {
 85    'Course 101': autograder.model.course.Course(
 86        id = 'course101',
 87        name = 'Course 101',
 88        assignments = ASSIGNMENTS['Course 101'],
 89    ),
 90    'Course Using Different Languages': autograder.model.course.Course(
 91        id = 'course-languages',
 92        name = 'Course Using Different Languages',
 93        assignments = ASSIGNMENTS['Course Using Different Languages'],
 94    ),
 95}
 96
 97COURSES_LIST: typing.List[autograder.model.course.Course] = sorted(COURSES.values())
 98
 99API_LOGS: typing.List[typing.Dict[str, typing.Any]] = [
100    {
101        "assignment": "hw0",
102        "course": "course101",
103        "level": -20,
104        "message": "Trace Course Log",
105        "timestamp": 100,
106        "user": "course-other@test.edulinq.org"
107    },
108    {
109        "level": -20,
110        "message": "Trace Server Log",
111        "timestamp": 150,
112        "user": "server-user@test.edulinq.org"
113    },
114    {
115        "assignment": "hw0",
116        "course": "course101",
117        "level": -10,
118        "message": "Debug Course Log",
119        "timestamp": 200,
120        "user": "course-student@test.edulinq.org"
121    },
122    {
123        "level": -10,
124        "message": "Debug Server Log",
125        "timestamp": 250,
126        "user": "server-creator@test.edulinq.org"
127    },
128    {
129        "assignment": "hw0",
130        "course": "course101",
131        "level": 0,
132        "message": "Info Course Log",
133        "timestamp": 300,
134        "user": "course-grader@test.edulinq.org"
135    },
136    {
137        "level": 0,
138        "message": "Info Server Log",
139        "timestamp": 350,
140        "user": "server-admin@test.edulinq.org"
141    },
142    {
143        "assignment": "hw0",
144        "course": "course101",
145        "error": "Course Warning",
146        "level": 10,
147        "message": "Warn Course Log",
148        "timestamp": 400,
149        "user": "course-admin@test.edulinq.org"
150    },
151    {
152        "error": "Server Warning",
153        "level": 10,
154        "message": "Warn Server Log",
155        "timestamp": 450,
156        "user": "server-owner@test.edulinq.org"
157    },
158    {
159        "course": "course101",
160        "error": "Course Error",
161        "level": 20,
162        "message": "Error Course Log",
163        "timestamp": 500,
164        "user": "course-owner@test.edulinq.org"
165    },
166    {
167        "error": "Server Error",
168        "level": 20,
169        "message": "Error Server Log",
170        "timestamp": 550
171    },
172    {
173        "course": "course101",
174        "level": 30,
175        "message": "Fatal Course Log",
176        "timestamp": 600
177    },
178    {
179        "level": 30,
180        "message": "Fatal Server Log",
181        "timestamp": 650
182    }
183]
184
185PARSED_LOGS: typing.List[autograder.model.log.LogRecord] = [autograder.model.log.LogRecord.from_api(record) for record in API_LOGS]
186
187# pylint: disable=line-too-long
188STACK_TRACE_PAYLOAD: typing.Dict[str, typing.Any] = {
189    "count": 11,
190    "stacks": [
191        {
192            "name": "goroutine 69",
193            "records": [
194                {
195                    "call": "github.com/edulinq/autograder/internal/util.getStackTraces(0x1)",
196                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/util/stack.go",
197                    "line": 47,
198                    "pointer": "+0x65"
199                },
200                {
201                    "call": "github.com/edulinq/autograder/internal/util.GetAllStackTraces(...)",
202                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/util/stack.go",
203                    "line": 38,
204                    "pointer": ""
205                },
206                {
207                    "call": "github.com/edulinq/autograder/internal/api/system.HandleStacks(0x0?)",
208                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/system/stacks.go",
209                    "line": 20,
210                    "pointer": "+0x19"
211                },
212                {
213                    "call": "reflect.Value.call({0xdab400?, 0x1012168?, 0xc0002340e0?}, {0xed5e4e, 0x4}, {0xc000541840, 0x1, 0xc000541858?})",
214                    "file": "/usr/lib/go/src/reflect/value.go",
215                    "line": 581,
216                    "pointer": "+0xcc6"
217                },
218                {
219                    "call": "reflect.Value.Call({0xdab400?, 0x1012168?, 0x0?}, {0xc000541840?, 0x0?, 0x0?})",
220                    "file": "/usr/lib/go/src/reflect/value.go",
221                    "line": 365,
222                    "pointer": "+0xb9"
223                },
224                {
225                    "call": "github.com/edulinq/autograder/internal/api/core.callHandler({0xdab400?, 0x1012168?}, {0xdd1c40?, 0xc00054c000?})",
226                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go",
227                    "line": 237,
228                    "pointer": "+0xf6"
229                },
230                {
231                    "call": "github.com/edulinq/autograder/internal/api/core.handleAPIEndpoint({0x1112390, 0xc0002e03c0}, 0xc000390a00, {0xdab400, 0x1012168})",
232                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go",
233                    "line": 116,
234                    "pointer": "+0x166"
235                },
236                {
237                    "call": "github.com/edulinq/autograder/internal/api/core.MustNewAPIRoute.func1({0x1112390, 0xc0002e03c0}, 0xc000390a00)",
238                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/route.go",
239                    "line": 87,
240                    "pointer": "+0xc5"
241                },
242                {
243                    "call": "github.com/edulinq/autograder/internal/api/core.(*BaseRoute).Handle(...)",
244                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/route.go",
245                    "line": 48,
246                    "pointer": ""
247                },
248                {
249                    "call": "github.com/edulinq/autograder/internal/api/core.ServeRoutes(0xc000328018, {0x1112390, 0xc0002e03c0}, 0xc000390a00)",
250                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go",
251                    "line": 72,
252                    "pointer": "+0x37d"
253                },
254                {
255                    "call": "github.com/edulinq/autograder/internal/api/server.createAPIServer.GetRouteServer.func1({0x1112390?, 0xc0002e03c0?}, 0x1?)",
256                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go",
257                    "line": 43,
258                    "pointer": "+0x2d"
259                },
260                {
261                    "call": "net/http.HandlerFunc.ServeHTTP(0x41f445?, {0x1112390?, 0xc0002e03c0?}, 0xc0002e0301?)",
262                    "file": "/usr/lib/go/src/net/http/server.go",
263                    "line": 2322,
264                    "pointer": "+0x29"
265                },
266                {
267                    "call": "net/http.serverHandler.ServeHTTP({0x110fbf8?}, {0x1112390?, 0xc0002e03c0?}, 0x6?)",
268                    "file": "/usr/lib/go/src/net/http/server.go",
269                    "line": 3340,
270                    "pointer": "+0x8e"
271                },
272                {
273                    "call": "net/http.(*conn).serve(0xc0005007e0, {0x1114498, 0xc0002f4a80})",
274                    "file": "/usr/lib/go/src/net/http/server.go",
275                    "line": 2109,
276                    "pointer": "+0x665"
277                },
278                {
279                    "call": "created by net/http.(*Server).Serve in goroutine 30",
280                    "file": "/usr/lib/go/src/net/http/server.go",
281                    "line": 3493,
282                    "pointer": "+0x485"
283                }
284            ],
285            "status": "running"
286        },
287        {
288            "name": "goroutine 1",
289            "records": [
290                {
291                    "call": "github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock(0xc0003b24c0, {0xedf2a7?, 0x0?})",
292                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
293                    "line": 68,
294                    "pointer": "+0x25f"
295                },
296                {
297                    "call": "github.com/edulinq/autograder/internal/procedures/server.RunAndBlock.RunAndBlockFull.func1(0xc00017de78, {0xedf2a7, 0xe}, 0xf8?)",
298                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go",
299                    "line": 113,
300                    "pointer": "+0x115"
301                },
302                {
303                    "call": "github.com/edulinq/autograder/internal/procedures/server.RunAndBlockFull(...)",
304                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go",
305                    "line": 118,
306                    "pointer": ""
307                },
308                {
309                    "call": "github.com/edulinq/autograder/internal/procedures/server.RunAndBlock({0xedf2a7?, 0x0?})",
310                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go",
311                    "line": 96,
312                    "pointer": "+0x2b"
313                },
314                {
315                    "call": "main.main()",
316                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/cmd/server/main.go",
317                    "line": 23,
318                    "pointer": "+0xf6"
319                }
320            ],
321            "status": "chan receive"
322        },
323        {
324            "name": "goroutine 7",
325            "records": [
326                {
327                    "call": "github.com/edulinq/autograder/internal/lockmanager.removeStaleLocks()",
328                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/lockmanager/lockmanager.go",
329                    "line": 108,
330                    "pointer": "+0x54"
331                },
332                {
333                    "call": "created by github.com/edulinq/autograder/internal/lockmanager.init.0 in goroutine 1",
334                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/lockmanager/lockmanager.go",
335                    "line": 28,
336                    "pointer": "+0x4c"
337                }
338            ],
339            "status": "chan receive"
340        },
341        {
342            "name": "goroutine 27",
343            "records": [
344                {
345                    "call": "github.com/shirou/gopsutil/v4/internal/common.Sleep({0x1113ef8, 0x1758900}, 0x0?)",
346                    "file": "/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/internal/common/sleep.go",
347                    "line": 13,
348                    "pointer": "+0x76"
349                },
350                {
351                    "call": "github.com/shirou/gopsutil/v4/cpu.PercentWithContext({0x1113ef8, 0x1758900}, 0xdf8475800, 0x0)",
352                    "file": "/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/cpu/cpu.go",
353                    "line": 164,
354                    "pointer": "+0x71"
355                },
356                {
357                    "call": "github.com/shirou/gopsutil/v4/cpu.Percent(...)",
358                    "file": "/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/cpu/cpu.go",
359                    "line": 150,
360                    "pointer": ""
361                },
362                {
363                    "call": "github.com/edulinq/autograder/internal/stats.getSystemMetrics(0xc00018b860?)",
364                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go",
365                    "line": 149,
366                    "pointer": "+0xc5"
367                },
368                {
369                    "call": "github.com/edulinq/autograder/internal/stats.collectSystemStats(0xea60)",
370                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go",
371                    "line": 62,
372                    "pointer": "+0xe8"
373                },
374                {
375                    "call": "created by github.com/edulinq/autograder/internal/stats.startSystemStatsCollection in goroutine 1",
376                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go",
377                    "line": 95,
378                    "pointer": "+0x137"
379                }
380            ],
381            "status": "select"
382        },
383        {
384            "name": "goroutine 29",
385            "records": [
386                {
387                    "call": "time.Sleep(0x1bf08eb000)",
388                    "file": "/usr/lib/go/src/runtime/time.go",
389                    "line": 363,
390                    "pointer": "+0x165"
391                },
392                {
393                    "call": "github.com/edulinq/autograder/internal/tasks.runTasks()",
394                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/tasks/core.go",
395                    "line": 58,
396                    "pointer": "+0x6f"
397                },
398                {
399                    "call": "created by github.com/edulinq/autograder/internal/tasks.Start in goroutine 1",
400                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/tasks/core.go",
401                    "line": 27,
402                    "pointer": "+0x7f"
403                }
404            ],
405            "status": "sleep"
406        },
407        {
408            "name": "goroutine 30",
409            "records": [
410                {
411                    "call": "internal/poll.runtime_pollWait(0x7f026465b600, 0x72)",
412                    "file": "/usr/lib/go/src/runtime/netpoll.go",
413                    "line": 351,
414                    "pointer": "+0x85"
415                },
416                {
417                    "call": "internal/poll.(*pollDesc).wait(0xc00048e080?, 0x900000036?, 0x0)",
418                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
419                    "line": 84,
420                    "pointer": "+0x27"
421                },
422                {
423                    "call": "internal/poll.(*pollDesc).waitRead(...)",
424                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
425                    "line": 89,
426                    "pointer": ""
427                },
428                {
429                    "call": "internal/poll.(*FD).Accept(0xc00048e080)",
430                    "file": "/usr/lib/go/src/internal/poll/fd_unix.go",
431                    "line": 613,
432                    "pointer": "+0x28c"
433                },
434                {
435                    "call": "net.(*netFD).accept(0xc00048e080)",
436                    "file": "/usr/lib/go/src/net/fd_unix.go",
437                    "line": 161,
438                    "pointer": "+0x29"
439                },
440                {
441                    "call": "net.(*TCPListener).accept(0xc0000b92c0)",
442                    "file": "/usr/lib/go/src/net/tcpsock_posix.go",
443                    "line": 159,
444                    "pointer": "+0x1b"
445                },
446                {
447                    "call": "net.(*TCPListener).Accept(0xc0000b92c0)",
448                    "file": "/usr/lib/go/src/net/tcpsock.go",
449                    "line": 380,
450                    "pointer": "+0x30"
451                },
452                {
453                    "call": "net/http.(*Server).Serve(0xc00037e100, {0x1112090, 0xc0000b92c0})",
454                    "file": "/usr/lib/go/src/net/http/server.go",
455                    "line": 3463,
456                    "pointer": "+0x30c"
457                },
458                {
459                    "call": "net/http.(*Server).ListenAndServe(0xc00037e100)",
460                    "file": "/usr/lib/go/src/net/http/server.go",
461                    "line": 3389,
462                    "pointer": "+0x72"
463                },
464                {
465                    "call": "github.com/edulinq/autograder/internal/api/server.runAPIServerInternal()",
466                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/http_socket.go",
467                    "line": 135,
468                    "pointer": "+0x85"
469                },
470                {
471                    "call": "github.com/edulinq/autograder/internal/api/server.runAPIServer(0xc000328018, 0xc000270ce0)",
472                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/http_socket.go",
473                    "line": 54,
474                    "pointer": "+0x152"
475                },
476                {
477                    "call": "github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func2()",
478                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
479                    "line": 48,
480                    "pointer": "+0x2f"
481                },
482                {
483                    "call": "created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1",
484                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
485                    "line": 47,
486                    "pointer": "+0x13f"
487                }
488            ],
489            "status": "IO wait"
490        },
491        {
492            "name": "goroutine 31",
493            "records": [
494                {
495                    "call": "internal/poll.runtime_pollWait(0x7f026465b800, 0x72)",
496                    "file": "/usr/lib/go/src/runtime/netpoll.go",
497                    "line": 351,
498                    "pointer": "+0x85"
499                },
500                {
501                    "call": "internal/poll.(*pollDesc).wait(0xc000037b00?, 0xc00036ed70?, 0x0)",
502                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
503                    "line": 84,
504                    "pointer": "+0x27"
505                },
506                {
507                    "call": "internal/poll.(*pollDesc).waitRead(...)",
508                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
509                    "line": 89,
510                    "pointer": ""
511                },
512                {
513                    "call": "internal/poll.(*FD).Accept(0xc000037b00)",
514                    "file": "/usr/lib/go/src/internal/poll/fd_unix.go",
515                    "line": 613,
516                    "pointer": "+0x28c"
517                },
518                {
519                    "call": "net.(*netFD).accept(0xc000037b00)",
520                    "file": "/usr/lib/go/src/net/fd_unix.go",
521                    "line": 161,
522                    "pointer": "+0x29"
523                },
524                {
525                    "call": "net.(*UnixListener).accept(0x41f445?)",
526                    "file": "/usr/lib/go/src/net/unixsock_posix.go",
527                    "line": 172,
528                    "pointer": "+0x16"
529                },
530                {
531                    "call": "net.(*UnixListener).Accept(0xc00028cf60)",
532                    "file": "/usr/lib/go/src/net/unixsock.go",
533                    "line": 260,
534                    "pointer": "+0x30"
535                },
536                {
537                    "call": "github.com/edulinq/autograder/internal/api/server.runUnixSocketServer(0xc000270ce0)",
538                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/unix_socket.go",
539                    "line": 49,
540                    "pointer": "+0x14f"
541                },
542                {
543                    "call": "github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func3()",
544                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
545                    "line": 52,
546                    "pointer": "+0x25"
547                },
548                {
549                    "call": "created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1",
550                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
551                    "line": 51,
552                    "pointer": "+0x199"
553                }
554            ],
555            "status": "IO wait"
556        },
557        {
558            "name": "goroutine 50",
559            "records": [
560                {
561                    "call": "os/signal.signal_recv()",
562                    "file": "/usr/lib/go/src/runtime/sigqueue.go",
563                    "line": 152,
564                    "pointer": "+0x29"
565                },
566                {
567                    "call": "os/signal.loop()",
568                    "file": "/usr/lib/go/src/os/signal/signal_unix.go",
569                    "line": 23,
570                    "pointer": "+0x13"
571                },
572                {
573                    "call": "created by os/signal.Notify.func1.1 in goroutine 1",
574                    "file": "/usr/lib/go/src/os/signal/signal.go",
575                    "line": 152,
576                    "pointer": "+0x1f"
577                }
578            ],
579            "status": "syscall"
580        },
581        {
582            "name": "goroutine 51",
583            "records": [
584                {
585                    "call": "github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func4()",
586                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
587                    "line": 61,
588                    "pointer": "+0x25"
589                },
590                {
591                    "call": "created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1",
592                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go",
593                    "line": 60,
594                    "pointer": "+0x23e"
595                }
596            ],
597            "status": "chan receive"
598        },
599        {
600            "name": "goroutine 70",
601            "records": [
602                {
603                    "call": "internal/poll.runtime_pollWait(0x7f026465b400, 0x72)",
604                    "file": "/usr/lib/go/src/runtime/netpoll.go",
605                    "line": 351,
606                    "pointer": "+0x85"
607                },
608                {
609                    "call": "internal/poll.(*pollDesc).wait(0xc0002f8100?, 0xc00026a361?, 0x0)",
610                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
611                    "line": 84,
612                    "pointer": "+0x27"
613                },
614                {
615                    "call": "internal/poll.(*pollDesc).waitRead(...)",
616                    "file": "/usr/lib/go/src/internal/poll/fd_poll_runtime.go",
617                    "line": 89,
618                    "pointer": ""
619                },
620                {
621                    "call": "internal/poll.(*FD).Read(0xc0002f8100, {0xc00026a361, 0x1, 0x1})",
622                    "file": "/usr/lib/go/src/internal/poll/fd_unix.go",
623                    "line": 165,
624                    "pointer": "+0x279"
625                },
626                {
627                    "call": "net.(*netFD).Read(0xc0002f8100, {0xc00026a361?, 0x1711cf0?, 0xc000524770?})",
628                    "file": "/usr/lib/go/src/net/fd_posix.go",
629                    "line": 68,
630                    "pointer": "+0x25"
631                },
632                {
633                    "call": "net.(*conn).Read(0xc00050e048, {0xc00026a361?, 0xc00005ac08?, 0xc000528000?})",
634                    "file": "/usr/lib/go/src/net/net.go",
635                    "line": 196,
636                    "pointer": "+0x45"
637                },
638                {
639                    "call": "net/http.(*connReader).backgroundRead(0xc00026a340)",
640                    "file": "/usr/lib/go/src/net/http/server.go",
641                    "line": 702,
642                    "pointer": "+0x33"
643                },
644                {
645                    "call": "created by net/http.(*connReader).startBackgroundRead in goroutine 69",
646                    "file": "/usr/lib/go/src/net/http/server.go",
647                    "line": 698,
648                    "pointer": "+0xb6"
649                }
650            ],
651            "status": "IO wait"
652        },
653        {
654            "name": "goroutine 60",
655            "records": [
656                {
657                    "call": "github.com/edulinq/autograder/internal/log.logToBackend.gowrap1()",
658                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/log/logging.go",
659                    "line": 122,
660                    "pointer": ""
661                },
662                {
663                    "call": "runtime.goexit({})",
664                    "file": "/usr/lib/go/src/runtime/asm_amd64.s",
665                    "line": 1693,
666                    "pointer": "+0x1"
667                },
668                {
669                    "call": "created by github.com/edulinq/autograder/internal/log.logToBackend in goroutine 69",
670                    "file": "/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/log/logging.go",
671                    "line": 122,
672                    "pointer": "+0x8a"
673                }
674            ],
675            "status": "runnable"
676        }
677    ]
678}
SERVER_USERS: Dict[str, lms.model.users.ServerUser] = {'course-admin': {"email": "course-admin@test.edulinq.org", "extra_fields": {}, "id": "course-admin@test.edulinq.org", "name": "course-admin"}, 'course-grader': {"email": "course-grader@test.edulinq.org", "extra_fields": {}, "id": "course-grader@test.edulinq.org", "name": "course-grader"}, 'course-other': {"email": "course-other@test.edulinq.org", "extra_fields": {}, "id": "course-other@test.edulinq.org", "name": "course-other"}, 'course-owner': {"email": "course-owner@test.edulinq.org", "extra_fields": {}, "id": "course-owner@test.edulinq.org", "name": "course-owner"}, 'course-student': {"email": "course-student@test.edulinq.org", "extra_fields": {}, "id": "course-student@test.edulinq.org", "name": "course-student"}, 'extra-course-student-1': {"email": "extra-course-student-1@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-1@test.edulinq.org", "name": "extra-course-student-1"}, 'extra-course-student-2': {"email": "extra-course-student-2@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-2@test.edulinq.org", "name": "extra-course-student-2"}, 'extra-course-student-3': {"email": "extra-course-student-3@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-3@test.edulinq.org", "name": "extra-course-student-3"}, 'extra-course-student-4': {"email": "extra-course-student-4@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-4@test.edulinq.org", "name": "extra-course-student-4"}, 'server-admin': {"email": "server-admin@test.edulinq.org", "extra_fields": {}, "id": "server-admin@test.edulinq.org", "name": "server-admin"}, 'server-creator': {"email": "server-creator@test.edulinq.org", "extra_fields": {}, "id": "server-creator@test.edulinq.org", "name": "server-creator"}, 'server-owner': {"email": "server-owner@test.edulinq.org", "extra_fields": {}, "id": "server-owner@test.edulinq.org", "name": "server-owner"}, 'server-user': {"email": "server-user@test.edulinq.org", "extra_fields": {}, "id": "server-user@test.edulinq.org", "name": "server-user"}}
COURSE_USERS: Dict[str, Dict[str, lms.model.users.CourseUser]] = {'Course 101': {'course-admin': {"email": "course-admin@test.edulinq.org", "extra_fields": {}, "id": "course-admin@test.edulinq.org", "name": "course-admin", "raw_role": null, "role": "admin"}, 'course-grader': {"email": "course-grader@test.edulinq.org", "extra_fields": {}, "id": "course-grader@test.edulinq.org", "name": "course-grader", "raw_role": null, "role": "grader"}, 'course-other': {"email": "course-other@test.edulinq.org", "extra_fields": {}, "id": "course-other@test.edulinq.org", "name": "course-other", "raw_role": null, "role": "other"}, 'course-owner': {"email": "course-owner@test.edulinq.org", "extra_fields": {}, "id": "course-owner@test.edulinq.org", "name": "course-owner", "raw_role": null, "role": "owner"}, 'course-student': {"email": "course-student@test.edulinq.org", "extra_fields": {}, "id": "course-student@test.edulinq.org", "name": "course-student", "raw_role": null, "role": "student"}}, 'Course Using Different Languages': {'course-admin': {"email": "course-admin@test.edulinq.org", "extra_fields": {}, "id": "course-admin@test.edulinq.org", "name": "course-admin", "raw_role": null, "role": "admin"}, 'course-grader': {"email": "course-grader@test.edulinq.org", "extra_fields": {}, "id": "course-grader@test.edulinq.org", "name": "course-grader", "raw_role": null, "role": "grader"}, 'course-other': {"email": "course-other@test.edulinq.org", "extra_fields": {}, "id": "course-other@test.edulinq.org", "name": "course-other", "raw_role": null, "role": "other"}, 'course-owner': {"email": "course-owner@test.edulinq.org", "extra_fields": {}, "id": "course-owner@test.edulinq.org", "name": "course-owner", "raw_role": null, "role": "owner"}, 'course-student': {"email": "course-student@test.edulinq.org", "extra_fields": {}, "id": "course-student@test.edulinq.org", "name": "course-student", "raw_role": null, "role": "student"}}, 'Extra Course': {'course-owner': {"email": "course-owner@test.edulinq.org", "extra_fields": {}, "id": "course-owner@test.edulinq.org", "name": "course-owner", "raw_role": null, "role": "owner"}, 'extra-course-student-1': {"email": "extra-course-student-1@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-1@test.edulinq.org", "name": "extra-course-student-1", "raw_role": null, "role": "student"}, 'extra-course-student-2': {"email": "extra-course-student-2@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-2@test.edulinq.org", "name": "extra-course-student-2", "raw_role": null, "role": "student"}, 'extra-course-student-3': {"email": "extra-course-student-3@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-3@test.edulinq.org", "name": "extra-course-student-3", "raw_role": null, "role": "student"}, 'extra-course-student-4': {"email": "extra-course-student-4@test.edulinq.org", "extra_fields": {}, "id": "extra-course-student-4@test.edulinq.org", "name": "extra-course-student-4", "raw_role": null, "role": "student"}}}
RAW_USER_DATA: Dict[str, Any] = {'course-admin@test.edulinq.org': {'email': 'course-admin@test.edulinq.org', 'name': 'course-admin', 'server-role': 'user', 'salt': 'c72f85ab94f66b49b4e93cb0d0776fc7', 'password': {'id': 'ada14ed3-cc9f-49d3-b910-bb5874fbcce0', 'hex-digest': '841adfc8e2020a678c383fb56e9459da77c75fb813ab9e443a582902de0fecf6', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'id': 'df0a1f16-9cd8-4395-8509-10ae314fe6fc', 'hex-digest': '74f72731825e660f5720ffe86e71103c5534171f1e4ef9fe2d8c73d0121ef192', 'source': 'user', 'name': 'test', 'creation-time': 0, 'access-time': 0}, {'_cleartext': 'd2932b918fd30f1011836112683e20de', 'access-time': 0, 'creation-time': 0, 'id': '01b437a6-0de0-4278-b87d-bcfa128cbf36', 'name': 'test', 'source': 'user', 'hex-digest': '1aebbbea41732c9e4e513a51074f11a2eed1ba59221aafb17faa6fbf01a1f27c'}], 'course-info': {'course-languages': {'role': 'admin', 'lms-id': 'lms-course-admin@test.edulinq.org'}, 'course101': {'role': 'admin', 'lms-id': 'lms-course-admin@test.edulinq.org'}}}, 'course-grader@test.edulinq.org': {'email': 'course-grader@test.edulinq.org', 'name': 'course-grader', 'server-role': 'user', 'salt': 'a853844be59b339ec45733eddeee41cd', 'password': {'id': 'fd22640e-9801-41c2-bd9e-2cb67781e42f', 'hex-digest': 'f511fa2bd83e5423b29356d798d20a214de9534e4d64fb3956a184b1a91cc66a', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '3ffd2f75491d257a668ce807271bccee', 'access-time': 0, 'creation-time': 0, 'id': 'ee954969-094c-461f-b4fd-23eb48f78ebf', 'name': 'test', 'source': 'user', 'hex-digest': 'b093533fdb2da72d7fb7bbae97d802e69bb2dbc574b60780614830d7c244c35d'}], 'course-info': {'course-languages': {'role': 'grader', 'lms-id': 'lms-course-grader@test.edulinq.org'}, 'course101': {'role': 'grader', 'lms-id': 'lms-course-grader@test.edulinq.org'}}}, 'course-other@test.edulinq.org': {'email': 'course-other@test.edulinq.org', 'name': 'course-other', 'server-role': 'user', 'salt': '72f9b3fdeec2724d3e47edb617295f95', 'password': {'id': '21030916-3a97-4cbc-820d-d8f2df109ff6', 'hex-digest': '0b3420a7c188254522f4aecebe427d0e3fda03067e6289b7524914de26cedb11', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '0ec8427e3d32c1b9c8632dda0f8f8b76', 'access-time': 0, 'creation-time': 0, 'id': '2e664056-e378-4bc5-9cb2-5f1539262ccb', 'name': 'test', 'source': 'user', 'hex-digest': '7a1f1f73e439a3892ca38b4e9a49a5aa3c0f0953ccbe3b43efbfea12a28bafc2'}], 'course-info': {'course-languages': {'role': 'other', 'lms-id': 'lms-course-other@test.edulinq.org'}, 'course101': {'role': 'other', 'lms-id': 'lms-course-other@test.edulinq.org'}}}, 'course-owner@test.edulinq.org': {'email': 'course-owner@test.edulinq.org', 'name': 'course-owner', 'server-role': 'user', 'salt': 'c61fe4f62b132d3908843e9c00f45c98', 'password': {'id': '2e1e1f71-0537-44b8-b3bd-e886e636ab0c', 'hex-digest': '200217c44f4c36e5d6380c1c6fb639a9f1749dc81a79b13261fa2cd5806f3d06', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '38ecd3a6f6e2152f0bb3dca0deacf640', 'access-time': 0, 'creation-time': 0, 'id': '2459a3d8-cd3b-43fa-9f39-cbf8dfd45175', 'name': 'test', 'source': 'user', 'hex-digest': '9ae4c11a7a91eabfcfb7d376ea13cc583f725e037563ff48ac83103942b939dc'}], 'course-info': {'course-languages': {'role': 'owner', 'lms-id': 'lms-course-owner@test.edulinq.org'}, 'course101': {'role': 'owner', 'lms-id': 'lms-course-owner@test.edulinq.org'}}}, 'course-student@test.edulinq.org': {'email': 'course-student@test.edulinq.org', 'name': 'course-student', 'server-role': 'user', 'salt': 'ea3a6c7d493148e01a8faff25956dc28', 'password': {'id': '04c6b410-cd17-48ec-a872-8d3ca5ecdb4a', 'hex-digest': '9188d2d8ec097dffd93ce06e16ab01d0344d648923fbc45535e93ad16c5d8c58', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '97beae436f093dd3cb6ddc632bce1d16', 'access-time': 0, 'creation-time': 0, 'id': 'dddbc97c-36e4-43fc-b5a0-478aade61c53', 'name': 'test', 'source': 'user', 'hex-digest': '0f08e9ed74e449ca800af8a5236eeeaa51eed428bd18fb968ff77d8b95bd1de6'}], 'course-info': {'course-languages': {'role': 'student', 'lms-id': 'lms-course-student@test.edulinq.org'}, 'course101': {'role': 'student', 'lms-id': 'lms-course-student@test.edulinq.org'}}}, 'extra-course-student-1@test.edulinq.org': {'email': 'extra-course-student-1@test.edulinq.org', 'name': 'extra-course-student-1', 'server-role': 'user', 'salt': 'e60c471719d04f5b980d5af0831582ca2c15f5595e655c52d14294b5ea1a7b20', 'password': {'id': '666d07f2-a13a-418d-8732-da11072db79f', 'hex-digest': '0baa24021610f2173f7af8284f6216021761526d639f7c219983d86b3b688a8f', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '562bd08851b21bbbf730e47991128d81', 'access-time': 0, 'creation-time': 0, 'id': '9217c24e-d56c-49bf-b32f-b0551732b47c', 'name': 'test', 'source': 'user', 'hex-digest': 'e95458cc4a8e7b262e9af2ccfa887da5c931c735098c5daa06afb7ed3a36fb99'}], 'course-info': {}}, 'extra-course-student-2@test.edulinq.org': {'email': 'extra-course-student-2@test.edulinq.org', 'name': 'extra-course-student-2', 'server-role': 'user', 'salt': '29b745d5382799830b04d1d425d488eb410f70fbd4b8b8666ce6afc6e50cfa69', 'password': {'id': 'ce677066-5d03-41c3-8aa8-12f97a63b63e', 'hex-digest': '153e14d56d67424eb4c5e3958caff40a1e75f00a1899ab70bd71b151e231a83b', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': 'bcd72b521cebe8751d78f1bbed517a4b', 'access-time': 0, 'creation-time': 0, 'id': '93189ccd-0a23-4b16-8f47-f7c80e9b9f9c', 'name': 'test', 'source': 'user', 'hex-digest': 'de7f71bacc3fa80e7f5e2df05cc73e9f35af4d0bbfb59d7ec234ea5e7ca6b426'}], 'course-info': {}}, 'extra-course-student-3@test.edulinq.org': {'email': 'extra-course-student-3@test.edulinq.org', 'name': 'extra-course-student-3', 'server-role': 'user', 'salt': '2f171d465e5291be9ea61e928cf25c77d9111a09cfbe1c6168be96abc7e5eec0', 'password': {'id': 'b13691fc-682e-4435-8098-017eb01bdd4c', 'hex-digest': '383755691755fd854f07508d1cb7f44a1353fd280103248b35a6e573e046fdd6', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '1680eb19d6904fe2f572df8ba012c6c4', 'access-time': 0, 'creation-time': 0, 'id': 'b4436119-0d80-4cdf-9fbd-24d77fb5ee65', 'name': 'test', 'source': 'user', 'hex-digest': '079ac0187a31f88dcd7eb97749ebb4831b8b4fe7fbf1cc15b8ceaef65da50780'}], 'course-info': {}}, 'extra-course-student-4@test.edulinq.org': {'email': 'extra-course-student-4@test.edulinq.org', 'name': 'extra-course-student-4', 'server-role': 'user', 'salt': '00a7db95a02681381116955b8cd48e7d52eeb3dc85e229ef99352f631547f335', 'password': {'id': '52b104c2-88b1-4343-afa3-03820b6b4c4c', 'hex-digest': 'a7792a1067a456cddc4ad06c78bd75f53309c116ba60150a0083847a9ad515ef', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': 'bf9f4203892fbba9dfe78c371ca08c02', 'access-time': 0, 'creation-time': 0, 'id': '3e2f642e-64bb-4da8-a861-ae71725c00d3', 'name': 'test', 'source': 'user', 'hex-digest': '59b5ae2dc7686c1c2d090d0c5e9733d09c85c6cc1a61d2e5810ac032d8c8a7bc'}], 'course-info': {}}, 'server-admin@test.edulinq.org': {'email': 'server-admin@test.edulinq.org', 'name': 'server-admin', 'server-role': 'admin', 'salt': '8c6d9026147e4a7c7168b690b1d23347cffe85e4723772ee7899a84f71d76356', 'password': {'id': '191cad61-cf41-4cc5-8684-7e10e6d052ca', 'hex-digest': 'fc4b34a5fc240db90cc3f1d81993a392c858afbce5e650dff2754eb54dbbabec', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '61aa86de8ccb3097583bf4324262ba7e', 'access-time': 0, 'creation-time': 0, 'id': 'db281f47-22a8-409b-be53-812534c80a8a', 'name': 'test', 'source': 'user', 'hex-digest': '72545679cad9aaf352077453613a71e66ca9b1ff31c8819933efe3212df1caf8'}], 'course-info': {}}, 'server-creator@test.edulinq.org': {'email': 'server-creator@test.edulinq.org', 'name': 'server-creator', 'server-role': 'creator', 'salt': 'dba7e9d474fdaa86096d09f1166700994a526b775eb2ce1295c351723e9323f6', 'password': {'id': '61199a8d-24bd-42fe-b045-d463f8a81f0a', 'hex-digest': 'f4e77e82622376eb2433e9f1b4bc0e8ae1fe4e85124c64f12d3353ca2dfe4454', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': 'bf0b99a5991bc67ef03c0fb9b302b448', 'access-time': 0, 'creation-time': 0, 'id': '44b25809-7ce8-4f9b-a73e-2459bc809309', 'name': 'test', 'source': 'user', 'hex-digest': '2711c34c69c0b113b04708dabc02b23e86f0368b9f576aad60affe70271808c0'}], 'course-info': {}}, 'server-owner@test.edulinq.org': {'email': 'server-owner@test.edulinq.org', 'name': 'server-owner', 'server-role': 'owner', 'salt': '079fc4ea12af4ba0ebfa131b3dd3de664b2cae1ac2b7c49ce4ec66d56b2acfa3', 'password': {'id': 'c16da2bc-38a1-4e40-8afc-b6c7788e977c', 'hex-digest': '802047ad02de27abbe567bfe4c038a3764e979b4c10a44a7a08bf3fcb2f74c9e', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': 'f1fe1332d4f84bd66a35897b41050a92', 'access-time': 0, 'creation-time': 0, 'id': 'a9ed41a5-9e2a-4db6-aa55-7a0cfdb6de34', 'name': 'test', 'source': 'user', 'hex-digest': '9538d06c3aa5a396984e921e7a68f2e9ebced7b10cc0d13b4197ac2393a6f30d'}], 'course-info': {}}, 'server-user@test.edulinq.org': {'email': 'server-user@test.edulinq.org', 'name': 'server-user', 'server-role': 'user', 'salt': 'c66b1d9533c87838e0c364bd014150691990ed007d59632cabce77206e8351a3', 'password': {'id': '5baab146-a6f1-445c-b79f-a135b7fe1f06', 'hex-digest': '8c95dbc49b86b732b53683cc0a535a512127c2f3a09c577a68a7ea632544c4b9', 'source': 'password', 'name': 'password', 'creation-time': 0, 'access-time': 0}, 'tokens': [{'_cleartext': '0b6e8b232098c05f407f4ef17f6ee74b', 'access-time': 0, 'creation-time': 0, 'id': 'f8d1e487-0574-4f8d-a610-a62ad252db88', 'name': 'test', 'source': 'user', 'hex-digest': 'ec8c4deeb07ee2d535f977117732b75fb83e4e09068ddc1797148add303c767f'}], 'course-info': {}}}
ASSIGNMENTS: Dict[str, Dict[str, lms.model.assignments.Assignment]] = {'Course 101': {'hw0': {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "hw0", "name": "Homework 0", "open_date": null, "points_possible": null}}, 'Course Using Different Languages': {'bash': {"close_date": null, "description": null, "due_date": 0, "extra_fields": {}, "id": "bash", "name": "A Simple Bash Assignment", "open_date": null, "points_possible": null}, 'cpp': {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "cpp", "name": "A Simple C++ Assignment", "open_date": null, "points_possible": null}, 'java': {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "java", "name": "A Simple Java Assignment", "open_date": null, "points_possible": null}}}
COURSES: Dict[str, autograder.model.course.Course] = {'Course 101': {"assignments": {"hw0": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "hw0", "name": "Homework 0", "open_date": null, "points_possible": null}}, "extra_fields": {}, "id": "course101", "name": "Course 101"}, 'Course Using Different Languages': {"assignments": {"bash": {"close_date": null, "description": null, "due_date": 0, "extra_fields": {}, "id": "bash", "name": "A Simple Bash Assignment", "open_date": null, "points_possible": null}, "cpp": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "cpp", "name": "A Simple C++ Assignment", "open_date": null, "points_possible": null}, "java": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "java", "name": "A Simple Java Assignment", "open_date": null, "points_possible": null}}, "extra_fields": {}, "id": "course-languages", "name": "Course Using Different Languages"}}
COURSES_LIST: List[autograder.model.course.Course] = [{"assignments": {"bash": {"close_date": null, "description": null, "due_date": 0, "extra_fields": {}, "id": "bash", "name": "A Simple Bash Assignment", "open_date": null, "points_possible": null}, "cpp": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "cpp", "name": "A Simple C++ Assignment", "open_date": null, "points_possible": null}, "java": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "java", "name": "A Simple Java Assignment", "open_date": null, "points_possible": null}}, "extra_fields": {}, "id": "course-languages", "name": "Course Using Different Languages"}, {"assignments": {"hw0": {"close_date": null, "description": null, "due_date": null, "extra_fields": {}, "id": "hw0", "name": "Homework 0", "open_date": null, "points_possible": null}}, "extra_fields": {}, "id": "course101", "name": "Course 101"}]
API_LOGS: List[Dict[str, Any]] = [{'assignment': 'hw0', 'course': 'course101', 'level': -20, 'message': 'Trace Course Log', 'timestamp': 100, 'user': 'course-other@test.edulinq.org'}, {'level': -20, 'message': 'Trace Server Log', 'timestamp': 150, 'user': 'server-user@test.edulinq.org'}, {'assignment': 'hw0', 'course': 'course101', 'level': -10, 'message': 'Debug Course Log', 'timestamp': 200, 'user': 'course-student@test.edulinq.org'}, {'level': -10, 'message': 'Debug Server Log', 'timestamp': 250, 'user': 'server-creator@test.edulinq.org'}, {'assignment': 'hw0', 'course': 'course101', 'level': 0, 'message': 'Info Course Log', 'timestamp': 300, 'user': 'course-grader@test.edulinq.org'}, {'level': 0, 'message': 'Info Server Log', 'timestamp': 350, 'user': 'server-admin@test.edulinq.org'}, {'assignment': 'hw0', 'course': 'course101', 'error': 'Course Warning', 'level': 10, 'message': 'Warn Course Log', 'timestamp': 400, 'user': 'course-admin@test.edulinq.org'}, {'error': 'Server Warning', 'level': 10, 'message': 'Warn Server Log', 'timestamp': 450, 'user': 'server-owner@test.edulinq.org'}, {'course': 'course101', 'error': 'Course Error', 'level': 20, 'message': 'Error Course Log', 'timestamp': 500, 'user': 'course-owner@test.edulinq.org'}, {'error': 'Server Error', 'level': 20, 'message': 'Error Server Log', 'timestamp': 550}, {'course': 'course101', 'level': 30, 'message': 'Fatal Course Log', 'timestamp': 600}, {'level': 30, 'message': 'Fatal Server Log', 'timestamp': 650}]
PARSED_LOGS: List[autograder.model.log.LogRecord] = [{"assignment": "hw0", "attributes": {}, "course": "course101", "error": null, "extra_fields": {}, "level": "TRACE", "message": "Trace Course Log", "timestamp": 100, "user": "course-other@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": null, "error": null, "extra_fields": {}, "level": "TRACE", "message": "Trace Server Log", "timestamp": 150, "user": "server-user@test.edulinq.org"}, {"assignment": "hw0", "attributes": {}, "course": "course101", "error": null, "extra_fields": {}, "level": "DEBUG", "message": "Debug Course Log", "timestamp": 200, "user": "course-student@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": null, "error": null, "extra_fields": {}, "level": "DEBUG", "message": "Debug Server Log", "timestamp": 250, "user": "server-creator@test.edulinq.org"}, {"assignment": "hw0", "attributes": {}, "course": "course101", "error": null, "extra_fields": {}, "level": "INFO", "message": "Info Course Log", "timestamp": 300, "user": "course-grader@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": null, "error": null, "extra_fields": {}, "level": "INFO", "message": "Info Server Log", "timestamp": 350, "user": "server-admin@test.edulinq.org"}, {"assignment": "hw0", "attributes": {}, "course": "course101", "error": "Course Warning", "extra_fields": {}, "level": "WARN", "message": "Warn Course Log", "timestamp": 400, "user": "course-admin@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": null, "error": "Server Warning", "extra_fields": {}, "level": "WARN", "message": "Warn Server Log", "timestamp": 450, "user": "server-owner@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": "course101", "error": "Course Error", "extra_fields": {}, "level": "ERROR", "message": "Error Course Log", "timestamp": 500, "user": "course-owner@test.edulinq.org"}, {"assignment": null, "attributes": {}, "course": null, "error": "Server Error", "extra_fields": {}, "level": "ERROR", "message": "Error Server Log", "timestamp": 550, "user": null}, {"assignment": null, "attributes": {}, "course": "course101", "error": null, "extra_fields": {}, "level": "FATAL", "message": "Fatal Course Log", "timestamp": 600, "user": null}, {"assignment": null, "attributes": {}, "course": null, "error": null, "extra_fields": {}, "level": "FATAL", "message": "Fatal Server Log", "timestamp": 650, "user": null}]
STACK_TRACE_PAYLOAD: Dict[str, Any] = {'count': 11, 'stacks': [{'name': 'goroutine 69', 'records': [{'call': 'github.com/edulinq/autograder/internal/util.getStackTraces(0x1)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/util/stack.go', 'line': 47, 'pointer': '+0x65'}, {'call': 'github.com/edulinq/autograder/internal/util.GetAllStackTraces(...)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/util/stack.go', 'line': 38, 'pointer': ''}, {'call': 'github.com/edulinq/autograder/internal/api/system.HandleStacks(0x0?)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/system/stacks.go', 'line': 20, 'pointer': '+0x19'}, {'call': 'reflect.Value.call({0xdab400?, 0x1012168?, 0xc0002340e0?}, {0xed5e4e, 0x4}, {0xc000541840, 0x1, 0xc000541858?})', 'file': '/usr/lib/go/src/reflect/value.go', 'line': 581, 'pointer': '+0xcc6'}, {'call': 'reflect.Value.Call({0xdab400?, 0x1012168?, 0x0?}, {0xc000541840?, 0x0?, 0x0?})', 'file': '/usr/lib/go/src/reflect/value.go', 'line': 365, 'pointer': '+0xb9'}, {'call': 'github.com/edulinq/autograder/internal/api/core.callHandler({0xdab400?, 0x1012168?}, {0xdd1c40?, 0xc00054c000?})', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go', 'line': 237, 'pointer': '+0xf6'}, {'call': 'github.com/edulinq/autograder/internal/api/core.handleAPIEndpoint({0x1112390, 0xc0002e03c0}, 0xc000390a00, {0xdab400, 0x1012168})', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go', 'line': 116, 'pointer': '+0x166'}, {'call': 'github.com/edulinq/autograder/internal/api/core.MustNewAPIRoute.func1({0x1112390, 0xc0002e03c0}, 0xc000390a00)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/route.go', 'line': 87, 'pointer': '+0xc5'}, {'call': 'github.com/edulinq/autograder/internal/api/core.(*BaseRoute).Handle(...)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/route.go', 'line': 48, 'pointer': ''}, {'call': 'github.com/edulinq/autograder/internal/api/core.ServeRoutes(0xc000328018, {0x1112390, 0xc0002e03c0}, 0xc000390a00)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go', 'line': 72, 'pointer': '+0x37d'}, {'call': 'github.com/edulinq/autograder/internal/api/server.createAPIServer.GetRouteServer.func1({0x1112390?, 0xc0002e03c0?}, 0x1?)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/core/routing.go', 'line': 43, 'pointer': '+0x2d'}, {'call': 'net/http.HandlerFunc.ServeHTTP(0x41f445?, {0x1112390?, 0xc0002e03c0?}, 0xc0002e0301?)', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 2322, 'pointer': '+0x29'}, {'call': 'net/http.serverHandler.ServeHTTP({0x110fbf8?}, {0x1112390?, 0xc0002e03c0?}, 0x6?)', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 3340, 'pointer': '+0x8e'}, {'call': 'net/http.(*conn).serve(0xc0005007e0, {0x1114498, 0xc0002f4a80})', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 2109, 'pointer': '+0x665'}, {'call': 'created by net/http.(*Server).Serve in goroutine 30', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 3493, 'pointer': '+0x485'}], 'status': 'running'}, {'name': 'goroutine 1', 'records': [{'call': 'github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock(0xc0003b24c0, {0xedf2a7?, 0x0?})', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 68, 'pointer': '+0x25f'}, {'call': 'github.com/edulinq/autograder/internal/procedures/server.RunAndBlock.RunAndBlockFull.func1(0xc00017de78, {0xedf2a7, 0xe}, 0xf8?)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go', 'line': 113, 'pointer': '+0x115'}, {'call': 'github.com/edulinq/autograder/internal/procedures/server.RunAndBlockFull(...)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go', 'line': 118, 'pointer': ''}, {'call': 'github.com/edulinq/autograder/internal/procedures/server.RunAndBlock({0xedf2a7?, 0x0?})', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/procedures/server/start.go', 'line': 96, 'pointer': '+0x2b'}, {'call': 'main.main()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/cmd/server/main.go', 'line': 23, 'pointer': '+0xf6'}], 'status': 'chan receive'}, {'name': 'goroutine 7', 'records': [{'call': 'github.com/edulinq/autograder/internal/lockmanager.removeStaleLocks()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/lockmanager/lockmanager.go', 'line': 108, 'pointer': '+0x54'}, {'call': 'created by github.com/edulinq/autograder/internal/lockmanager.init.0 in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/lockmanager/lockmanager.go', 'line': 28, 'pointer': '+0x4c'}], 'status': 'chan receive'}, {'name': 'goroutine 27', 'records': [{'call': 'github.com/shirou/gopsutil/v4/internal/common.Sleep({0x1113ef8, 0x1758900}, 0x0?)', 'file': '/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/internal/common/sleep.go', 'line': 13, 'pointer': '+0x76'}, {'call': 'github.com/shirou/gopsutil/v4/cpu.PercentWithContext({0x1113ef8, 0x1758900}, 0xdf8475800, 0x0)', 'file': '/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/cpu/cpu.go', 'line': 164, 'pointer': '+0x71'}, {'call': 'github.com/shirou/gopsutil/v4/cpu.Percent(...)', 'file': '/home/edulinq/go/pkg/mod/github.com/shirou/gopsutil/v4@v4.24.11/cpu/cpu.go', 'line': 150, 'pointer': ''}, {'call': 'github.com/edulinq/autograder/internal/stats.getSystemMetrics(0xc00018b860?)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go', 'line': 149, 'pointer': '+0xc5'}, {'call': 'github.com/edulinq/autograder/internal/stats.collectSystemStats(0xea60)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go', 'line': 62, 'pointer': '+0xe8'}, {'call': 'created by github.com/edulinq/autograder/internal/stats.startSystemStatsCollection in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/stats/system.go', 'line': 95, 'pointer': '+0x137'}], 'status': 'select'}, {'name': 'goroutine 29', 'records': [{'call': 'time.Sleep(0x1bf08eb000)', 'file': '/usr/lib/go/src/runtime/time.go', 'line': 363, 'pointer': '+0x165'}, {'call': 'github.com/edulinq/autograder/internal/tasks.runTasks()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/tasks/core.go', 'line': 58, 'pointer': '+0x6f'}, {'call': 'created by github.com/edulinq/autograder/internal/tasks.Start in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/tasks/core.go', 'line': 27, 'pointer': '+0x7f'}], 'status': 'sleep'}, {'name': 'goroutine 30', 'records': [{'call': 'internal/poll.runtime_pollWait(0x7f026465b600, 0x72)', 'file': '/usr/lib/go/src/runtime/netpoll.go', 'line': 351, 'pointer': '+0x85'}, {'call': 'internal/poll.(*pollDesc).wait(0xc00048e080?, 0x900000036?, 0x0)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 84, 'pointer': '+0x27'}, {'call': 'internal/poll.(*pollDesc).waitRead(...)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 89, 'pointer': ''}, {'call': 'internal/poll.(*FD).Accept(0xc00048e080)', 'file': '/usr/lib/go/src/internal/poll/fd_unix.go', 'line': 613, 'pointer': '+0x28c'}, {'call': 'net.(*netFD).accept(0xc00048e080)', 'file': '/usr/lib/go/src/net/fd_unix.go', 'line': 161, 'pointer': '+0x29'}, {'call': 'net.(*TCPListener).accept(0xc0000b92c0)', 'file': '/usr/lib/go/src/net/tcpsock_posix.go', 'line': 159, 'pointer': '+0x1b'}, {'call': 'net.(*TCPListener).Accept(0xc0000b92c0)', 'file': '/usr/lib/go/src/net/tcpsock.go', 'line': 380, 'pointer': '+0x30'}, {'call': 'net/http.(*Server).Serve(0xc00037e100, {0x1112090, 0xc0000b92c0})', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 3463, 'pointer': '+0x30c'}, {'call': 'net/http.(*Server).ListenAndServe(0xc00037e100)', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 3389, 'pointer': '+0x72'}, {'call': 'github.com/edulinq/autograder/internal/api/server.runAPIServerInternal()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/http_socket.go', 'line': 135, 'pointer': '+0x85'}, {'call': 'github.com/edulinq/autograder/internal/api/server.runAPIServer(0xc000328018, 0xc000270ce0)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/http_socket.go', 'line': 54, 'pointer': '+0x152'}, {'call': 'github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func2()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 48, 'pointer': '+0x2f'}, {'call': 'created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 47, 'pointer': '+0x13f'}], 'status': 'IO wait'}, {'name': 'goroutine 31', 'records': [{'call': 'internal/poll.runtime_pollWait(0x7f026465b800, 0x72)', 'file': '/usr/lib/go/src/runtime/netpoll.go', 'line': 351, 'pointer': '+0x85'}, {'call': 'internal/poll.(*pollDesc).wait(0xc000037b00?, 0xc00036ed70?, 0x0)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 84, 'pointer': '+0x27'}, {'call': 'internal/poll.(*pollDesc).waitRead(...)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 89, 'pointer': ''}, {'call': 'internal/poll.(*FD).Accept(0xc000037b00)', 'file': '/usr/lib/go/src/internal/poll/fd_unix.go', 'line': 613, 'pointer': '+0x28c'}, {'call': 'net.(*netFD).accept(0xc000037b00)', 'file': '/usr/lib/go/src/net/fd_unix.go', 'line': 161, 'pointer': '+0x29'}, {'call': 'net.(*UnixListener).accept(0x41f445?)', 'file': '/usr/lib/go/src/net/unixsock_posix.go', 'line': 172, 'pointer': '+0x16'}, {'call': 'net.(*UnixListener).Accept(0xc00028cf60)', 'file': '/usr/lib/go/src/net/unixsock.go', 'line': 260, 'pointer': '+0x30'}, {'call': 'github.com/edulinq/autograder/internal/api/server.runUnixSocketServer(0xc000270ce0)', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/unix_socket.go', 'line': 49, 'pointer': '+0x14f'}, {'call': 'github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func3()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 52, 'pointer': '+0x25'}, {'call': 'created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 51, 'pointer': '+0x199'}], 'status': 'IO wait'}, {'name': 'goroutine 50', 'records': [{'call': 'os/signal.signal_recv()', 'file': '/usr/lib/go/src/runtime/sigqueue.go', 'line': 152, 'pointer': '+0x29'}, {'call': 'os/signal.loop()', 'file': '/usr/lib/go/src/os/signal/signal_unix.go', 'line': 23, 'pointer': '+0x13'}, {'call': 'created by os/signal.Notify.func1.1 in goroutine 1', 'file': '/usr/lib/go/src/os/signal/signal.go', 'line': 152, 'pointer': '+0x1f'}], 'status': 'syscall'}, {'name': 'goroutine 51', 'records': [{'call': 'github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock.func4()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 61, 'pointer': '+0x25'}, {'call': 'created by github.com/edulinq/autograder/internal/api/server.(*APIServer).RunAndBlock in goroutine 1', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/api/server/server.go', 'line': 60, 'pointer': '+0x23e'}], 'status': 'chan receive'}, {'name': 'goroutine 70', 'records': [{'call': 'internal/poll.runtime_pollWait(0x7f026465b400, 0x72)', 'file': '/usr/lib/go/src/runtime/netpoll.go', 'line': 351, 'pointer': '+0x85'}, {'call': 'internal/poll.(*pollDesc).wait(0xc0002f8100?, 0xc00026a361?, 0x0)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 84, 'pointer': '+0x27'}, {'call': 'internal/poll.(*pollDesc).waitRead(...)', 'file': '/usr/lib/go/src/internal/poll/fd_poll_runtime.go', 'line': 89, 'pointer': ''}, {'call': 'internal/poll.(*FD).Read(0xc0002f8100, {0xc00026a361, 0x1, 0x1})', 'file': '/usr/lib/go/src/internal/poll/fd_unix.go', 'line': 165, 'pointer': '+0x279'}, {'call': 'net.(*netFD).Read(0xc0002f8100, {0xc00026a361?, 0x1711cf0?, 0xc000524770?})', 'file': '/usr/lib/go/src/net/fd_posix.go', 'line': 68, 'pointer': '+0x25'}, {'call': 'net.(*conn).Read(0xc00050e048, {0xc00026a361?, 0xc00005ac08?, 0xc000528000?})', 'file': '/usr/lib/go/src/net/net.go', 'line': 196, 'pointer': '+0x45'}, {'call': 'net/http.(*connReader).backgroundRead(0xc00026a340)', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 702, 'pointer': '+0x33'}, {'call': 'created by net/http.(*connReader).startBackgroundRead in goroutine 69', 'file': '/usr/lib/go/src/net/http/server.go', 'line': 698, 'pointer': '+0xb6'}], 'status': 'IO wait'}, {'name': 'goroutine 60', 'records': [{'call': 'github.com/edulinq/autograder/internal/log.logToBackend.gowrap1()', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/log/logging.go', 'line': 122, 'pointer': ''}, {'call': 'runtime.goexit({})', 'file': '/usr/lib/go/src/runtime/asm_amd64.s', 'line': 1693, 'pointer': '+0x1'}, {'call': 'created by github.com/edulinq/autograder/internal/log.logToBackend in goroutine 69', 'file': '/home/edulinq/code/autograder/autograder-py/testdata/autograder-testdata/autograder-server/internal/log/logging.go', 'line': 122, 'pointer': '+0x8a'}], 'status': 'runnable'}]}