go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/copyright/copyright_test.go (about) 1 /* 2 3 Copyright (c) 2023 - Present. Will Charczuk. All rights reserved. 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository. 5 6 */ 7 8 package copyright 9 10 import ( 11 "bytes" 12 "context" 13 "fmt" 14 "io" 15 "io/fs" 16 "os" 17 "path/filepath" 18 "strings" 19 "testing" 20 "text/template" 21 "time" 22 23 "go.charczuk.com/sdk/assert" 24 ) 25 26 func Test_Copyright_GetStdout(t *testing.T) { 27 c := new(Copyright) 28 assert.ItsEqual(t, os.Stdout, c.GetStdout()) 29 buf := new(bytes.Buffer) 30 c.Stdout = buf 31 assert.ItsEqual(t, c.Stdout, c.GetStdout()) 32 c.Quiet = true 33 assert.ItsEqual(t, io.Discard, c.GetStdout()) 34 } 35 36 func Test_Copyright_GetStderr(t *testing.T) { 37 c := new(Copyright) 38 assert.ItsEqual(t, os.Stderr, c.GetStderr()) 39 buf := new(bytes.Buffer) 40 c.Stderr = buf 41 assert.ItsEqual(t, buf, c.GetStderr()) 42 c.Quiet = true 43 assert.ItsEqual(t, io.Discard, c.GetStderr()) 44 } 45 46 func Test_Copyright_mergeFileSections(t *testing.T) { 47 merged := new(Copyright).mergeFileSections([]byte("foo"), []byte("bar"), []byte("baz")) 48 assert.ItsEqual(t, "foobarbaz", string(merged)) 49 } 50 51 func Test_Copyright_fileHasCopyrightHeader(t *testing.T) { 52 var goodCorpus = []byte(`foo 53 bar 54 baz 55 `) 56 57 notice, err := generateGoNotice(withYear(2021)) 58 assert.ItsNil(t, err) 59 60 goodCorpusWithNotice := Copyright{}.mergeFileSections([]byte(notice), goodCorpus) 61 assert.ItContains(t, string(goodCorpusWithNotice), "Copyright (c) 2021") 62 assert.ItsTrue(t, (Copyright{}).fileHasCopyrightHeader(goodCorpusWithNotice, []byte(notice))) 63 } 64 65 func Test_Copyright_fileHasCopyrightHeader_invalid(t *testing.T) { 66 c := new(Copyright) 67 68 var invalidCorpus = []byte(`foo 69 bar 70 baz 71 `) 72 expectedNotice, err := generateGoNotice(withYear(2021)) 73 assert.ItsNil(t, err) 74 75 assert.ItsFalse(t, c.fileHasCopyrightHeader(invalidCorpus, []byte(expectedNotice)), "we haven't added the notice") 76 } 77 78 func Test_Copyright_fileHasCopyrightHeader_differentYear(t *testing.T) { 79 c := new(Copyright) 80 81 var goodCorpus = []byte(`foo 82 bar 83 baz 84 `) 85 86 notice, err := generateGoNotice(withYear(2020)) 87 assert.ItsNil(t, err) 88 89 goodCorpusWithNotice := c.mergeFileSections(notice, goodCorpus) 90 assert.ItContains(t, string(goodCorpusWithNotice), "Copyright (c) 2020") 91 92 newNotice, err := generateGoNotice(withYear(2021)) 93 assert.ItsNil(t, err) 94 95 assert.ItsTrue(t, c.fileHasCopyrightHeader(goodCorpusWithNotice, []byte(newNotice))) 96 } 97 98 func Test_Copyright_fileHasCopyrightHeader_leadingWhitespace(t *testing.T) { 99 c := Copyright{} 100 101 var goodCorpus = []byte(`foo 102 bar 103 baz 104 `) 105 106 notice, err := generateGoNotice(withYear(2021)) 107 assert.ItsNil(t, err) 108 109 goodCorpusWithNotice := c.mergeFileSections([]byte("\n\n"), notice, goodCorpus) 110 assert.ItHasPrefix(t, string(goodCorpusWithNotice), "\n\n") 111 assert.ItContains(t, string(goodCorpusWithNotice), "Copyright (c) 2021") 112 113 assert.ItsTrue(t, c.fileHasCopyrightHeader(goodCorpusWithNotice, []byte(notice))) 114 } 115 116 func Test_Copyright_goBuildTagMatch(t *testing.T) { 117 118 c := Copyright{} 119 120 buildTag := []byte(`// +build foo 121 122 `) 123 corpus := []byte(`foo 124 bar 125 baz 126 `) 127 128 file := (Copyright{}).mergeFileSections(buildTag, corpus) 129 130 assert.ItsFalse(t, goBuildTagMatch.Match(corpus)) 131 assert.ItsTrue(t, goBuildTagMatch.Match(c.mergeFileSections(buildTag))) 132 133 found := goBuildTagMatch.FindAll(file, -1) 134 assert.ItsNotEmpty(t, found) 135 assert.ItsTrue(t, goBuildTagMatch.Match(file)) 136 } 137 138 func Test_Copyright_goBuildTagsMatch(t *testing.T) { 139 140 file := []byte(goBuildTags1) // testutil.GetTestFixture(its, "buildtags1.go") 141 assert.ItsTrue(t, goBuildTagMatch.Match(file)) 142 found := goBuildTagMatch.Find(file) 143 assert.ItsEqual(t, "//go:build tag1\n// +build tag1\n\n", string(found)) 144 145 file2 := []byte(goBuildTags2) // testutil.GetTestFixture(its, "buildtags2.go") 146 assert.ItsTrue(t, goBuildTagMatch.Match(file2)) 147 found2 := goBuildTagMatch.Find(file2) 148 149 expected := `// +build tag5 150 //go:build tag1 && tag2 && tag3 151 // +build tag1,tag2,tag3 152 // +build tag6 153 154 ` 155 assert.ItsEqual(t, expected, string(found2)) 156 157 file3 := []byte(goBuildTags3) // testutil.GetTestFixture(its, "buildtags3.go") 158 assert.ItsTrue(t, goBuildTagMatch.Match(file3)) 159 found3 := goBuildTagMatch.Find(file3) 160 assert.ItsEqual(t, "//go:build tag1 & tag2\n\n", string(found3)) 161 } 162 163 func Test_Copyright_goInjectNotice(t *testing.T) { 164 165 c := Copyright{} 166 167 file := []byte(`foo 168 bar 169 baz 170 `) 171 172 notice, err := generateGoNotice(withYear(2021)) 173 assert.ItsNil(t, err) 174 175 output := c.goInjectNotice("foo.go", file, notice) 176 assert.ItContains(t, string(output), "Copyright (c) 2021") 177 assert.ItHasSuffix(t, string(output), string(file)) 178 } 179 180 func Test_Copyright_goInjectNotice_buildTag(t *testing.T) { 181 182 c := Copyright{} 183 184 buildTag := []byte(`// +build foo`) 185 corpus := []byte(`foo 186 bar 187 baz 188 `) 189 190 file := c.mergeFileSections(buildTag, []byte("\n\n"), corpus) 191 192 notice, err := generateGoNotice(withYear(2021)) 193 assert.ItsNil(t, err) 194 195 output := c.goInjectNotice("foo.go", file, notice) 196 assert.ItContains(t, string(output), "Copyright (c) 2021") 197 assert.ItHasPrefix(t, string(output), string(buildTag)+"\n") 198 assert.ItHasSuffix(t, string(output), string(corpus)) 199 200 outputRepeat := c.goInjectNotice("foo.go", output, notice) 201 assert.ItsEmpty(t, outputRepeat, "inject notice functions should return an empty slice if the header already exists") 202 } 203 204 func Test_Copyright_goInjectNotice_goBuildTags(t *testing.T) { 205 type testCase struct { 206 Name string 207 Input string 208 Expect string 209 } 210 211 cases := []testCase{ 212 { 213 Name: "standard build tags", 214 Input: goBuildTags1, // "buildtags1.go", 215 Expect: goldenGoBuildTags1, 216 }, 217 { 218 Name: "multiple build tags", 219 Input: goBuildTags2, // "buildtags2.go", 220 Expect: goldenGoBuildTags2, 221 }, 222 { 223 Name: "build tags split across file", 224 Input: goBuildTags3, // "buildtags3.go", 225 Expect: goldenGoBuildTags3, 226 }, 227 } 228 229 for _, tc := range cases { 230 tc := tc 231 t.Run(tc.Name, func(t *testing.T) { 232 c := new(Copyright) 233 234 notice, err := generateGoNotice(withYear(2001)) 235 assert.ItsNil(t, err) 236 237 output := c.goInjectNotice("foo.go", []byte(tc.Input), notice) 238 assert.ItsEqual(t, string(output), tc.Expect) // testutil.AssertGoldenFile(it, output, tc.TestFile) 239 240 outputRepeat := c.goInjectNotice("foo.go", output, notice) 241 assert.ItsEmpty(t, outputRepeat) 242 }) 243 } 244 } 245 246 func Test_Copyright_tsInjectNotice_tsReferenceTags(t *testing.T) { 247 t.Parallel() 248 249 type testCase struct { 250 Name string 251 Input string 252 Expect string 253 } 254 255 cases := []testCase{ 256 { 257 Name: "single reference tag", 258 Input: tsReferenceTag, 259 Expect: goldenTsReferenceTag, 260 }, 261 { 262 Name: "multiple reference tags", 263 Input: tsReferenceTags, 264 Expect: goldenTsReferenceTags, 265 }, 266 { 267 Name: "no reference tags", 268 Input: tsTest, // "buildtags3.go", 269 Expect: goldenTs, 270 }, 271 } 272 273 for _, tc := range cases { 274 tc := tc 275 t.Run(tc.Name, func(t *testing.T) { 276 c := new(Copyright) 277 278 notice, err := generateTypescriptNotice(withYear(2022)) 279 assert.ItsNil(t, err) 280 281 output := c.tsInjectNotice("foo.ts", []byte(tc.Input), notice) 282 assert.ItsEqual(t, tc.Expect, string(output)) 283 284 outputRepeat := c.tsInjectNotice("foo.ts", output, notice) 285 assert.ItsEmpty(t, outputRepeat) 286 }) 287 } 288 } 289 290 func Test_Copyright_injectNotice_typescript(t *testing.T) { 291 c := new(Copyright) 292 293 file := []byte(`foo 294 bar 295 baz 296 `) 297 298 notice, err := generateTypescriptNotice(withYear(2001)) 299 assert.ItsNil(t, err) 300 301 output := c.injectNotice("foo.ts", file, notice) 302 assert.ItContains(t, string(output), "Copyright (c) 2001") 303 assert.ItHasSuffix(t, string(output), string(file)) 304 305 outputRepeat := c.injectNotice("foo.ts", output, notice) 306 assert.ItsEmpty(t, outputRepeat, "inject notice functions should return an empty slice if the header already exists") 307 } 308 309 func Test_Copyright_injectNotice_typescript_referenceTags(t *testing.T) { 310 311 c := Copyright{} 312 313 file := []byte(tsReferenceTags) 314 315 notice, err := generateTypescriptNotice(withYear(2001)) 316 assert.ItsNil(t, err) 317 318 output := c.injectNotice("foo.ts", file, notice) 319 assert.ItContains(t, string(output), "Copyright (c) 2001") 320 assert.ItHasSuffix(t, string(output), string(file)) 321 322 outputRepeat := c.injectNotice("foo.ts", output, notice) 323 assert.ItsEmpty(t, outputRepeat, "inject notice functions should return an empty slice if the header already exists") 324 } 325 326 func Test_Copyright_goInjectNotice_openSource(t *testing.T) { 327 c := &Copyright{ 328 Config: Config{ 329 Year: 2021, 330 License: "Apache 2.0", 331 RestrictionsTemplate: DefaultRestrictionsTemplate, 332 }, 333 } 334 335 file := []byte(`foo 336 bar 337 baz 338 `) 339 340 notice, err := generateGoNotice(c) 341 assert.ItsNil(t, err) 342 343 output := c.goInjectNotice("foo.go", file, notice) 344 assert.ItContains(t, string(output), "Copyright (c) 2021") 345 assert.ItContains(t, string(output), "Use of this source code is governed by a Apache 2.0") 346 assert.ItHasSuffix(t, string(output), string(file)) 347 } 348 349 func Test_Copyright_GetNoticeTemplate(t *testing.T) { 350 c := Copyright{} 351 352 noticeTemplate, ok := c.injectionTemplateByExtension(".js") 353 assert.ItsTrue(t, ok) 354 assert.ItsEqual(t, jsInjectionTemplate, noticeTemplate) 355 356 // it handles no dot prefix 357 noticeTemplate, ok = c.injectionTemplateByExtension("js") 358 assert.ItsTrue(t, ok) 359 assert.ItsEqual(t, jsInjectionTemplate, noticeTemplate) 360 361 // it handles another file type 362 noticeTemplate, ok = c.injectionTemplateByExtension(".go") 363 assert.ItsTrue(t, ok) 364 assert.ItsEqual(t, goInjectionTemplate, noticeTemplate) 365 366 noticeTemplate, ok = c.injectionTemplateByExtension("not-a-real-extension") 367 assert.ItsFalse(t, ok) 368 assert.ItsEmpty(t, noticeTemplate) 369 370 noticeTemplate, ok = c.injectionTemplateByExtension("not-a-real-extension") 371 assert.ItsFalse(t, ok) 372 assert.ItsEmpty(t, noticeTemplate) 373 } 374 375 type mockInfoDir string 376 377 func (mid mockInfoDir) Name() string { return string(mid) } 378 func (mid mockInfoDir) Size() int64 { return 1 << 8 } 379 func (mid mockInfoDir) Mode() fs.FileMode { return fs.FileMode(0755) } 380 func (mid mockInfoDir) ModTime() time.Time { return time.Now().UTC() } 381 func (mid mockInfoDir) IsDir() bool { return true } 382 func (mid mockInfoDir) Sys() interface{} { return nil } 383 384 type mockInfoFile string 385 386 func (mif mockInfoFile) Name() string { return string(mif) } 387 func (mif mockInfoFile) Size() int64 { return 1 << 8 } 388 func (mif mockInfoFile) Mode() fs.FileMode { return fs.FileMode(0755) } 389 func (mif mockInfoFile) ModTime() time.Time { return time.Now().UTC() } 390 func (mif mockInfoFile) IsDir() bool { return false } 391 func (mif mockInfoFile) Sys() interface{} { return nil } 392 393 func Test_Copyright_includeOrExclude(t *testing.T) { 394 t.Parallel() 395 396 testCases := [...]struct { 397 Config Config 398 Path string 399 Info fs.FileInfo 400 Expected error 401 }{ 402 /*0*/ {Config: Config{}, Path: ".", Info: mockInfoDir("."), Expected: ErrWalkSkip}, 403 /*1*/ {Config: Config{Excludes: []string{"/foo/**"}}, Path: "/foo/bar", Info: mockInfoDir("bar"), Expected: filepath.SkipDir}, 404 /*2*/ {Config: Config{Excludes: []string{"/foo/**"}}, Path: "/foo/bar/baz.jpg", Info: mockInfoFile("baz.jpg"), Expected: ErrWalkSkip}, 405 /*3*/ {Config: Config{Includes: []string{"/foo/bar/*.jpg"}}, Path: "/foo/bar/baz.jpg", Info: mockInfoFile("baz.jpg"), Expected: nil}, 406 /*4*/ {Config: Config{Excludes: []string{}, Includes: []string{}}, Path: "/foo/bar/baz.jpg", Info: mockInfoFile("baz.jpg"), Expected: ErrWalkSkip}, 407 /*5*/ {Config: Config{}, Path: "/foo/bar/baz.jpg", Info: mockInfoFile("baz.jpg"), Expected: nil}, 408 /*6*/ {Config: Config{}, Path: "/foo/bar/baz.jpg", Info: mockInfoDir("baz"), Expected: ErrWalkSkip}, 409 } 410 411 for index, tc := range testCases { 412 c := Copyright{Config: tc.Config} 413 assert.ItsEqual(t, tc.Expected, c.includeOrExclude(".", tc.Path, tc.Info), fmt.Sprintf("test %d", index)) 414 } 415 } 416 417 const ( 418 tsFile0 = `import * as axios from 'axios';` 419 tsFile1 = `/// <reference path="../types/testing.d.ts" /> 420 /// <reference path="../types/something.d.ts" /> 421 /// <reference path="../types/somethingElse.d.ts" /> 422 /// <reference path="../types/somethingMore.d.ts" /> 423 /// <reference path="../types/somethingLess.d.ts" /> 424 425 import * as axios from 'axios'; 426 ` 427 428 pyFile0 = `from __future__ import print_function 429 430 import logging 431 import os 432 import shutil 433 import sys 434 import requests 435 import uuid 436 import json` 437 438 goFile0 = `// +build tools 439 package tools 440 441 import ( 442 // goimports organizes imports for us 443 _ "golang.org/x/tools/cmd/goimports" 444 445 // golint is an opinionated linter 446 _ "golang.org/x/lint/golint" 447 448 // ineffassign is an opinionated linter 449 _ "github.com/gordonklaus/ineffassign" 450 451 // staticcheck is ineffassign but better 452 _ "honnef.co/go/tools/cmd/staticcheck" 453 ) 454 ` 455 ) 456 457 func Test_Copyright_Walk(t *testing.T) { 458 tempDir, revert := createTestFS(t) 459 defer revert() 460 461 c := &Copyright{ 462 Config: Config{ 463 CopyrightHolder: string(testCopyrightHolder), 464 Includes: []string{"*.py", "*.ts"}, 465 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 466 }, 467 } 468 469 var err error 470 var seen []string 471 err = c.Walk(context.TODO(), func(path string, info os.FileInfo, file, notice []byte) error { 472 seen = append(seen, path) 473 return nil 474 }, tempDir) 475 assert.ItsNil(t, err) 476 expected := []string{ 477 filepath.Join(tempDir, "bar", "foo", "file0.py"), 478 filepath.Join(tempDir, "bar", "foo", "file0.ts"), 479 filepath.Join(tempDir, "bar", "foo", "file1.ts"), 480 filepath.Join(tempDir, "file0.py"), 481 filepath.Join(tempDir, "file0.ts"), 482 filepath.Join(tempDir, "file1.ts"), 483 filepath.Join(tempDir, "foo", "bar", "file0.py"), 484 filepath.Join(tempDir, "foo", "bar", "file0.ts"), 485 filepath.Join(tempDir, "foo", "bar", "file1.ts"), 486 } 487 assert.ItsEqual(t, expected, seen) 488 } 489 490 func Test_Copyright_Walk_noExitFirst(t *testing.T) { 491 tempDir, revert := createTestFS(t) 492 defer revert() 493 494 c := &Copyright{ 495 Config: Config{ 496 CopyrightHolder: string(testCopyrightHolder), 497 Includes: []string{"*.py", "*.ts"}, 498 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 499 ExitFirst: false, 500 }, 501 } 502 503 var err error 504 var seen []string 505 err = c.Walk(context.TODO(), func(path string, info os.FileInfo, file, notice []byte) error { 506 seen = append(seen, path) 507 if len(seen) > 0 { 508 return ErrFailure 509 } 510 return nil 511 }, tempDir) 512 assert.ItsNotNil(t, err) 513 expected := []string{ 514 filepath.Join(tempDir, "bar", "foo", "file0.py"), 515 filepath.Join(tempDir, "bar", "foo", "file0.ts"), 516 filepath.Join(tempDir, "bar", "foo", "file1.ts"), 517 filepath.Join(tempDir, "file0.py"), 518 filepath.Join(tempDir, "file0.ts"), 519 filepath.Join(tempDir, "file1.ts"), 520 filepath.Join(tempDir, "foo", "bar", "file0.py"), 521 filepath.Join(tempDir, "foo", "bar", "file0.ts"), 522 filepath.Join(tempDir, "foo", "bar", "file1.ts"), 523 } 524 assert.ItsEqual(t, expected, seen) 525 } 526 527 func Test_Copyright_Walk_exitFirst(t *testing.T) { 528 tempDir, revert := createTestFS(t) 529 defer revert() 530 531 c := &Copyright{ 532 Config: Config{ 533 CopyrightHolder: string(testCopyrightHolder), 534 Includes: []string{"*.py", "*.ts"}, 535 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 536 ExitFirst: true, 537 }, 538 } 539 540 var err error 541 var seen []string 542 err = c.Walk(context.TODO(), func(path string, info os.FileInfo, file, notice []byte) error { 543 seen = append(seen, path) 544 if len(seen) > 0 { 545 return ErrFailure 546 } 547 return nil 548 }, tempDir) 549 assert.ItsNotNil(t, err) 550 expected := []string{ 551 filepath.Join(tempDir, "bar", "foo", "file0.py"), 552 } 553 assert.ItsEqual(t, expected, seen) 554 } 555 556 func Test_Copyright_Inject(t *testing.T) { 557 tempDir, revert := createTestFS(t) 558 defer revert() 559 560 c := &Copyright{ 561 Config: Config{ 562 CopyrightHolder: string(testCopyrightHolder), 563 Includes: []string{"*.py", "*.ts", "*.go"}, 564 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 565 }, 566 } 567 568 err := c.Inject(context.TODO(), tempDir) 569 assert.ItsNil(t, err) 570 571 contents, err := os.ReadFile(filepath.Join(tempDir, "bar", "foo", "file0.py")) 572 assert.ItsNil(t, err) 573 assert.ItHasPrefix(t, string(contents), "#\n# Copyright") 574 575 contents, err = os.ReadFile(filepath.Join(tempDir, "bar", "foo", "file0.ts")) 576 assert.ItsNil(t, err) 577 assert.ItHasPrefix(t, string(contents), "/**\n * Copyright") 578 } 579 580 func Test_Copyright_Inject_Shebang(t *testing.T) { 581 tempDir, err := os.MkdirTemp("", "copyright_test") 582 assert.ItsNil(t, err) 583 t.Cleanup(func() { os.RemoveAll(tempDir) }) 584 585 // Write `shift.py` without 586 contents := strings.Join([]string{ 587 "\r\t", 588 " #!/usr/bin/env python", 589 "", 590 "def main():", 591 ` print("Hello world")`, 592 "", 593 "", 594 `if __name__ == "__main__":`, 595 " main()", 596 "", 597 }, "\n") 598 filename := filepath.Join(tempDir, "shift.py") 599 err = os.WriteFile(filename, []byte(contents), 0755) 600 assert.ItsNil(t, err) 601 602 // Actually inject 603 c := &Copyright{ 604 Config: Config{ 605 Year: 2021, 606 CopyrightHolder: string(testCopyrightHolder), 607 Includes: []string{"*shift.py"}, 608 RestrictionsTemplate: DefaultRestrictionsTemplateInternal, 609 }, 610 } 611 err = c.Inject(context.TODO(), tempDir) 612 assert.ItsNil(t, err) 613 614 // Verify injected contents are as expected 615 contentInjected, err := os.ReadFile(filename) 616 assert.ItsNil(t, err) 617 expected := strings.Join([]string{ 618 "\r\t", 619 " #!/usr/bin/env python", 620 "#", 621 "# " + expectedNoticePrefix(t), 622 "# " + mustRenderTemplate(DefaultRestrictionsTemplateInternal, testCopyrightHolder), 623 "#", 624 "", 625 "", 626 "def main():", 627 ` print("Hello world")`, 628 "", 629 "", 630 `if __name__ == "__main__":`, 631 " main()", 632 "", 633 }, "\n") 634 assert.ItsEqual(t, expected, string(contentInjected)) 635 636 // Verify no-op if notice header is already present 637 err = c.Inject(context.TODO(), tempDir) 638 assert.ItsNil(t, err) 639 contentInjected, err = os.ReadFile(filename) 640 assert.ItsNil(t, err) 641 assert.ItsEqual(t, expected, string(contentInjected)) 642 } 643 644 func Test_Copyright_Verify(t *testing.T) { 645 tempDir, revert := createTestFS(t) 646 defer revert() 647 648 c := &Copyright{ 649 Config: Config{ 650 CopyrightHolder: string(testCopyrightHolder), 651 Includes: []string{"*.py", "*.ts", "*.go"}, 652 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 653 }, 654 } 655 c.Stdout = new(bytes.Buffer) 656 c.Stderr = new(bytes.Buffer) 657 658 err := c.Verify(context.TODO(), tempDir) 659 assert.ItsNotNil(t, err, "we must record a failure from walking the test fs") 660 661 err = c.Inject(context.TODO(), tempDir) 662 assert.ItsNil(t, err) 663 664 err = c.Verify(context.TODO(), tempDir) 665 assert.ItsNil(t, err) 666 } 667 668 func Test_Copyright_Verify_Shebang(t *testing.T) { 669 tempDir, err := os.MkdirTemp("", "copyright_test") 670 assert.ItsNil(t, err) 671 t.Cleanup(func() { os.RemoveAll(tempDir) }) 672 673 // Write `shift.py` already injected 674 contents := strings.Join([]string{ 675 "#!/usr/bin/env python", 676 "#", 677 "# " + expectedNoticePrefix(t), 678 "# " + mustRenderTemplate(DefaultRestrictionsTemplateInternal, testCopyrightHolder), 679 "#", 680 "", 681 "", 682 "def main():", 683 ` print("Hello world")`, 684 "", 685 "", 686 `if __name__ == "__main__":`, 687 " main()", 688 "", 689 }, "\n") 690 filename := filepath.Join(tempDir, "shift.py") 691 err = os.WriteFile(filename, []byte(contents), 0755) 692 assert.ItsNil(t, err) 693 694 // Verify present 695 cfg := Config{ 696 CopyrightHolder: string(testCopyrightHolder), 697 Includes: []string{"*shift.py"}, 698 Quiet: true, 699 RestrictionsTemplate: DefaultRestrictionsTemplateInternal, 700 ShowDiff: false, 701 Year: 2021, 702 } 703 c := &Copyright{Config: cfg} 704 err = c.Verify(context.TODO(), tempDir) 705 assert.ItsNil(t, err) 706 707 // Write without and fail 708 contents = strings.Join([]string{ 709 "#!/usr/bin/env python", 710 "def main():", 711 ` print("Hello world")`, 712 "", 713 "", 714 `if __name__ == "__main__":`, 715 " main()", 716 "", 717 }, "\n") 718 err = os.WriteFile(filename, []byte(contents), 0755) 719 assert.ItsNil(t, err) 720 err = c.Verify(context.TODO(), tempDir) 721 assert.ItsEqual(t, "failure; one or more steps failed", fmt.Sprintf("%v", err)) 722 } 723 724 func Test_Copyright_Remove(t *testing.T) { 725 tempDir, revert := createTestFS(t) 726 defer revert() 727 728 c := &Copyright{ 729 Config: Config{ 730 CopyrightHolder: string(testCopyrightHolder), 731 Includes: []string{"*.py", "*.ts", "*.go"}, 732 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 733 }, 734 } 735 c.Stdout = new(bytes.Buffer) 736 c.Stderr = new(bytes.Buffer) 737 738 err := c.Inject(context.TODO(), tempDir) 739 assert.ItsNil(t, err) 740 741 err = c.Verify(context.TODO(), tempDir) 742 assert.ItsNil(t, err) 743 744 err = c.Remove(context.TODO(), tempDir) 745 assert.ItsNil(t, err) 746 747 err = c.Verify(context.TODO(), tempDir) 748 assert.ItsNotNil(t, err) 749 } 750 751 func Test_Copyright_Remove_Shebang(t *testing.T) { 752 tempDir, err := os.MkdirTemp("", "copyright_test") 753 assert.ItsNil(t, err) 754 t.Cleanup(func() { os.RemoveAll(tempDir) }) 755 756 // Write `shift.py` already injected 757 contents := strings.Join([]string{ 758 "#!/usr/bin/env python", 759 "#", 760 "# " + expectedNoticePrefix(t), 761 "# " + mustRenderTemplate(DefaultRestrictionsTemplateInternal, testCopyrightHolder), 762 "#", 763 "", 764 "", 765 "def main():", 766 ` print("Hello world")`, 767 "", 768 "", 769 `if __name__ == "__main__":`, 770 " main()", 771 "", 772 }, "\n") 773 filename := filepath.Join(tempDir, "shift.py") 774 err = os.WriteFile(filename, []byte(contents), 0755) 775 assert.ItsNil(t, err) 776 777 // Actually remove 778 c := &Copyright{ 779 Config: Config{ 780 Year: 2021, 781 CopyrightHolder: string(testCopyrightHolder), 782 Includes: []string{"*shift.py"}, 783 RestrictionsTemplate: DefaultRestrictionsTemplateInternal, 784 }, 785 } 786 err = c.Remove(context.TODO(), tempDir) 787 assert.ItsNil(t, err) 788 789 // Verify removed contents are as expected 790 contentRemoved, err := os.ReadFile(filename) 791 assert.ItsNil(t, err) 792 expected := strings.Join([]string{ 793 "#!/usr/bin/env python", 794 "def main():", 795 ` print("Hello world")`, 796 "", 797 "", 798 `if __name__ == "__main__":`, 799 " main()", 800 "", 801 }, "\n") 802 assert.ItsEqual(t, expected, string(contentRemoved)) 803 804 // Verify no-op if notice header is already removed 805 err = c.Remove(context.TODO(), tempDir) 806 assert.ItsNil(t, err) 807 contentRemoved, err = os.ReadFile(filename) 808 assert.ItsNil(t, err) 809 assert.ItsEqual(t, expected, string(contentRemoved)) 810 } 811 812 func Test_Copyright_Walk_singleFileRoot(t *testing.T) { 813 tempDir, revert := createTestFS(t) 814 defer revert() 815 816 c := Copyright{ 817 Config: Config{ 818 CopyrightHolder: string(testCopyrightHolder), 819 Includes: []string{"*.py", "*.ts"}, 820 Excludes: []string{"*/not-bar/*", "*/not-foo/*"}, 821 }, 822 } 823 824 var err error 825 var seen []string 826 err = c.Walk(context.TODO(), func(path string, info os.FileInfo, file, notice []byte) error { 827 seen = append(seen, path) 828 return nil 829 }, filepath.Join(tempDir, "file0.py")) 830 assert.ItsNil(t, err) 831 expected := []string{ 832 filepath.Join(tempDir, "file0.py"), 833 } 834 assert.ItsEqual(t, expected, seen) 835 } 836 837 func expectedNoticePrefix(t *testing.T) string { 838 vars := map[string]string{ 839 "Year": "2021", 840 "CopyrightHolder": string(testCopyrightHolder), 841 } 842 tmpl := template.New("output") 843 _, err := tmpl.Parse(DefaultNoticeTemplate) 844 assert.ItsNil(t, err) 845 prefixBuffer := new(bytes.Buffer) 846 err = tmpl.Execute(prefixBuffer, vars) 847 assert.ItsNil(t, err) 848 return strings.TrimRight(prefixBuffer.String(), "\n") 849 }