package common /* * This file is part of Autogits. * * Copyright © 2024 SUSE LLC * * Autogits is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 2 of the License, or (at your option) any later * version. * * Autogits is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * Foobar. If not, see . */ import ( "bufio" "bytes" "os" "os/exec" "path" "slices" "strings" "testing" ) func TestGitClone(t *testing.T) { tests := []struct { name string repo string branch string remoteName string remoteUrl string }{ { name: "Basic clone", repo: "pkgAclone", branch: "main", remoteName: "pkgA_main", remoteUrl: "/pkgA", }, { name: "Remote branch is non-existent", repo: "pkgAclone", branch: "main_not_here", remoteName: "pkgA_main", remoteUrl: "/pkgA", }, } return execPath, err := os.Getwd() if err != nil { t.Fatal(err) } d := t.TempDir() os.Chdir(d) defer os.Chdir(execPath) cmd := exec.Command(path.Join(execPath, "test_clone_setup.sh")) if _, err := cmd.Output(); err != nil { t.Fatal(err) } gh, err := AllocateGitWorkTree(d, "Test", "test@example.com") if err != nil { t.Fatal(err) } for _, test := range tests { t.Run(test.name, func(t *testing.T) { g, err := gh.CreateGitHandler("org") if err != nil { t.Fatal(err) } if _, err := g.GitClone(test.repo, test.branch, "file://"+d+test.remoteUrl); err != nil { t.Fatal(err) } id, err := g.GitBranchHead(test.repo, test.branch) if err != nil { t.Fatal(err) } t.Fatal(id) }) } } func TestGitMsgParsing(t *testing.T) { t.Run("tree message with size 56", func(t *testing.T) { const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00" data := make(chan byte, 500) for _, b := range []byte(hdr) { data <- b } gitHdr, err := parseGitMsg(data) if err != nil { t.Error(err) } if gitHdr.hash != "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98" { t.Errorf("Invalid hash %s", gitHdr.hash) } if gitHdr.size != 56 { t.Errorf("Invalid msg size: %d", gitHdr.size) } if gitHdr.itemType != "tree" { t.Errorf("Invalid msg type: %s", gitHdr.itemType) } }) t.Run("commit message with size 256", func(t *testing.T) { const hdr = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 256\x00" data := make(chan byte, 500) for _, b := range []byte(hdr) { data <- b } gitHdr, err := parseGitMsg(data) if err != nil { t.Error(err) } if gitHdr.hash != "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99" { t.Errorf("Invalid hash %s", gitHdr.hash) } if gitHdr.size != 256 { t.Errorf("Invalid msg size: %d", gitHdr.size) } if gitHdr.itemType != "commit" { t.Errorf("Invalid msg type: %s", gitHdr.itemType) } }) t.Run("invalid tree message with size 56", func(t *testing.T) { const hdr = "f408r8ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f98 tree 56\x00" data := make(chan byte, 500) for _, b := range []byte(hdr) { data <- b } gitHdr, err := parseGitMsg(data) if err.Error() != "Invalid character during object hash parse 'r' at 4" { t.Error(err) } if gitHdr.hash != "" { t.Errorf("Invalid hash %s", gitHdr.hash) } if gitHdr.size != 0 { t.Errorf("Invalid msg size: %d", gitHdr.size) } if gitHdr.itemType != "" { t.Errorf("Invalid msg type: %s", gitHdr.itemType) } }) } func TestGitCommitParsing(t *testing.T) { t.Run("parse valid commit message", func(t *testing.T) { const commitData = "f40888ea4515fe2e8eea617a16f5f50a45f652d894de3ad181d58de3aafb8f99 commit 253\000" + `tree e20033df9f18780756ba4a96dbc7eb1a626253961039cb674156f266ba7a4e53 parent 429cc2fe02170ca5668f0461928c7e7430c7a17cd64ac298286d7162572a7703 author Adam Majer 1720709149 +0200 committer Adam Majer 1720709149 +0200 .` + "\000" ch := make(chan byte, 5000) for _, b := range []byte(commitData) { ch <- b } commit, err := parseGitCommit(ch) if err != nil { t.Error(err) } if commit.Tree != "e20033df9f18780756ba4a96dbc7eb1a626253961039cb674156f266ba7a4e53" { t.Errorf("Invalid commit object: %#v", commit) } if commit.Msg != "." { t.Errorf("Invalid commit msg: '%s'", commit.Msg) } }) t.Run("parse multiline headers", func(t *testing.T) { const commitData = "cae5831ab48470ff060a5aaa12eb6e5a7acaf91e commit 1492\000" + `tree 1f9c8fe8099615d6d3921528402ac53f09213b02 parent e08a654fae0ecc91678819e0b62a2e014bad3339 author Yagiz Nizipli 1720967314 -0400 committer GitHub 1720967314 +0200 gpgsig -----BEGIN PGP SIGNATURE----- wsFcBAABCAAQBQJmk+CSCRC1aQ7uu5UhlAAAQIYQAEQXCl3bUUuegiz5/oitIIF7 6xhndcjQIuqY4dONIeOARrGwbKh8OtMHpfJhMRUmvWvXrsTA6P1PWl0YcyyIMzHZ a4sBsWyxA0uSztVywpvksvk6EdMoEXeXrHS3cBxePsH8bI+Pwnsv27PsevEwpyIT reB4zZsoGySFVqf2lnXxG5hSRMYw++BDXSDMZk2BP9BvueRXasJ0lT1c7HlbHepF TWzwyHZ91OhXjrdPY7qLQEEV/frwuM+UrxOPb2e83ZTg81vXFuugURfhHNx4Iu+F LCMvOeaF2vO5yJtMe8+tY1l0Wb8S1aWcGCECN2XCXmmnWxt+yYh2gjqxq3y0DMcz zvg6arQIepDFLkQPZMDlUCIjIJQn4FbAaQAvoyMF8Pi5YmxhRqgo3iOB5SBE8eES 63ifZ311izuSdD+o3ObFpzLoTgq62kglwfegZN/X8CzTSIqrT1norYJEbSwkWID1 WeRHUHfC7f6N3XK8zeb83zmhBU58ghW9sp5/LcefGMRJmVhBWhjBCpeMUaFHdKhl /dfgPl5gJrrJ+wM3O6iaay0R1Iv4Upe/yXrbQnIgGj/qqgMLEPBY8lzNYimVTLxd 2ObrcXnERo3wwxeUgWaAARbEGQjC51DK/2SXxVGUh+IokicsBNRKU7lVWwwFFely ntjtge6Gs9pA5rSIilPH =V1bK -----END PGP SIGNATURE----- meta: change email address of anonrig PR-URL: https://github.com/nodejs/node/pull/53829 Reviewed-By: Antoine du Hamel Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina Reviewed-By: Ulises Gascón Reviewed-By: Richard Lau Reviewed-By: Marco Ippolito ` + "\000" ch := make(chan byte, 5000) for _, b := range []byte(commitData) { ch <- b } commit, err := parseGitCommit(ch) if err != nil { t.Error(err) } if commit.Tree != "1f9c8fe8099615d6d3921528402ac53f09213b02" { t.Errorf("Invalid commit object: %#v", commit) } if commit.Msg[len(commit.Msg)-55:] != "Reviewed-By: Marco Ippolito " { t.Errorf("Invalid commit msg: '%s'", commit.Msg[len(commit.Msg)-55:]) } }) t.Run("parse multiline headers", func(t *testing.T) { const commitData = "c07c52c57a10fb355956df3caad2986613838f149274fbe312ad76560764829d commit 1150\000" + `tree 3e06b280ea056141ed5e8af9794a41ae5281930c45321803eab53a240cb60044 parent 19362a2cecb1fd25a89e03611d08ac68dcb1732f9dc0a68a40926356787fa4ca author Adrian Schröter 1746600403 +0200 committer Adrian Schröter 1746600403 +0200 gpgsig-sha256 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE1QF1zm/pNbvyhgLFkY2MlUwI22cFAmgbAd0ACgkQkY2MlUwI 22dxtA//eUCzIqxVdaEnOrFeTyxKig/mCOjaAyctmwr0vXUyElRtjXe4TzVG3QtR uDfhIrKYLZ2tU/0TewTW/4XopWxLuqEzVQLrjuYl7K5P3GoYk52W1yGT0szzm7/i 87j4UdRL9YGU/gYO7nSzstcfTP6AcmYzVUoOnwYR0K2vyOVjO4niL3mFXxLkIgIt jd82xcE4JpQz9Yjyq2nDdz4A55kLAwsqY+dOct4oC6bZmj1/JeoGQfPvUsvsQgcI syCHVh0GBxjvSv50V/VPzxQTFMal/TdtvAD4kmP/9RDi/5THzus8Peam8pV0gEIC Q15ZcuLwIsC9i7ifUDYgzLgBBRdpSI0qji4Y6clWULPVjsyghgyfQw1trBSySpC8 O1XfajUM+rXyrBLP6kzY+zl/zyzRdJ8JhljmC+SmNuyyEB77Hkn83k0f+aBhhqC2 4b3fIsKtwJZ1w6gr6SSz1BottiT9ShQzRaL8iRoF/2l5MkHPR+QFg2J7EIBqCbCQ hFUjdvWAXQBWkkTQlJmLmJBXDOLQg3o6xCbnZM0gPFjZWE7e3Mpky7H0+xPnoeg9 ukuvkexXQ6yrdiekA7HRLc76Te/I0m7KDOOWZ3rbJV6uH/3ps4FbLQTZO12AtZ6J n8hYdYfw9yjCxiKUjnEtXtDRe8DJpqv+hO0Wj4MI5gIA2JE2lzY= =Keg5 -----END PGP SIGNATURE----- dummy change, don't merge ` + "\000" ch := make(chan byte) go func() { for _, b := range []byte(commitData) { ch <- b } }() commit, err := parseGitCommit(ch) if err != nil { t.Error(err) } if commit.Tree != "3e06b280ea056141ed5e8af9794a41ae5281930c45321803eab53a240cb60044" { t.Errorf("Invalid commit object: %#v", commit) } if commit.Msg != "dummy change, don't merge\n" { t.Errorf("Invalid commit msg: '%s'", commit.Msg) } }) t.Run("parse tree object", func(t *testing.T) { const treeData = "\x31\x61\x30\x35\x64\x62\x37\x33\x36\x39\x33\x37\x34\x33\x30\x65\x31\x38\x64\x66\x34\x33\x61\x32\x37\x61\x39\x38\x30\x30\x31\x30\x31\x32\x65\x31\x65\x64\x32\x30\x34\x38\x32\x39\x38\x36\x37\x31\x32\x38\x66\x32\x63\x65\x38\x34\x30\x36\x62\x35\x63\x66\x63\x39\x20\x74\x72\x65\x65\x20\x32\x30\x35\x00\x34\x30\x30\x30\x30\x20\x62\x6f\x74\x73\x2d\x63\x6f\x6d\x6d\x6f\x6e\x00\x93\x17\xaa\x47\xf6\xea\x37\xe8\xbc\xe2\x80\x77\x57\x90\xf4\xa8\x01\xd7\xe3\x70\x2f\x84\xfb\xe1\xb0\x0e\x4a\x2c\x1c\x75\x2c\x2b\x34\x30\x30\x30\x30\x20\x6f\x62\x73\x2d\x73\x74\x61\x67\x69\x6e\x67\x2d\x62\x6f\x74\x00\x79\x77\x8b\x28\x7d\x37\x10\x59\xb9\x71\x28\x36\xed\x20\x31\x5f\xfb\xe1\xed\xb5\xba\x4f\x5e\xbb\x65\x65\x68\x23\x77\x32\x58\xfe\x34\x30\x30\x30\x30\x20\x70\x72\x2d\x72\x65\x76\x69\x65\x77\x00\x36\x0d\x45\xcb\x76\xb8\x93\xb3\x21\xba\xfa\xd5\x00\x9d\xfc\x59\xab\x88\xc1\x3c\x81\xcb\x48\x5a\xe0\x29\x29\x0f\xe3\x6b\x3c\x5e\x34\x30\x30\x30\x30\x20\x70\x72\x6a\x67\x69\x74\x2d\x75\x70\x64\x61\x74\x65\x72\x00\xb4\x0b\x1c\xf5\xfb\xec\x9a\xb2\x9f\x48\x3e\x21\x18\x0d\x51\xb7\x98\x6e\x21\x99\x74\x84\x67\x71\x41\x24\x42\xfc\xc9\x04\x12\x99\x00" ch := make(chan byte, 1000) for _, b := range []byte(treeData) { ch <- b } tree, err := parseGitTree(ch) if err != nil { t.Error(err) } found := false t.Log(tree.items) for _, item := range tree.items { if item.name == "bots-common" && item.hash == "9317aa47f6ea37e8bce280775790f4a801d7e3702f84fbe1b00e4a2c1c752c2b" && item.isTree() { found = true t.Log("found") break } } if !found { t.Error("expected sub-tree not found") } }) t.Run("parse tree object with submodules", func(t *testing.T) { const treeData = "\x34\x38\x34\x66\x31\x62\x65\x65\x63\x39\x35\x63\x61\x36\x62\x38\x36\x30\x64\x30\x64\x37\x63\x35\x65\x34\x38\x37\x31\x36\x36\x62\x30\x38\x65\x31\x61\x34\x35\x36\x37\x36\x62\x31\x37\x64\x39\x65\x65\x66\x32\x39\x39\x39\x65\x39\x64\x37\x34\x63\x62\x63\x64\x36\x20\x74\x72\x65\x65\x20\x33\x34\x39\x00\x31\x30\x30\x36\x34\x34\x20\x2e\x67\x69\x74\x6d\x6f\x64\x75\x6c\x65\x73\x00\xc8\x1e\x14\x29\xc5\xd4\x07\xfc\x32\xf1\xd7\xe2\x64\xee\x88\xfc\xce\xfc\x44\xf7\xae\x2e\xc4\x6e\x2c\x15\x27\x26\x65\xd4\xb8\x78\x31\x30\x30\x36\x34\x34\x20\x52\x45\x41\x44\x4d\x45\x2e\x6d\x64\x00\x90\x22\x84\x13\xbf\xd3\x5c\xfb\x1e\x27\x6b\xd6\x17\x3e\x89\xed\x0f\xc6\x31\x85\x24\x58\x6d\x9d\xf0\x6a\x1e\x17\x57\x19\x8d\xc2\x31\x36\x30\x30\x30\x30\x20\x6d\x69\x6e\x67\x77\x33\x32\x2d\x67\x63\x63\x00\xdc\x55\xb8\x28\x32\x8c\x8e\x49\x4f\x67\x87\x4a\x7d\x8c\x03\xdd\x1c\x6b\x4e\x02\xd1\x6b\x86\xe0\x8e\x47\xd7\x0e\xcd\x79\x96\x80\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x2d\x63\x6f\x6d\x6d\x6f\x6e\x00\xd6\xa7\x4c\x08\x40\x6c\xe4\x0c\xc8\xf7\xbf\xf2\xd5\xcf\x30\x90\x87\xa8\x72\x83\x61\xcc\x75\x35\x4b\x08\x62\xba\x50\x81\x93\xb8\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x32\x31\x00\x24\xee\x6b\xee\x74\x59\xa3\x86\xda\xda\xbf\x8a\x9f\x6a\xe4\xfa\x15\xc3\xf8\x10\xbf\xa0\x1c\xee\x52\x38\x13\x8a\xa2\x14\xd1\x80\x31\x36\x30\x30\x30\x30\x20\x6e\x6f\x64\x65\x6a\x73\x32\x32\x00\x87\x3a\x32\x3b\x26\x2e\xbb\x3b\xd7\x7b\x25\x92\xb2\xe1\x1b\xdd\x08\xdb\xc7\x21\xcb\xf4\xac\x9f\x97\x63\x7e\x58\xe1\xff\xfc\xe7\x31\x36\x30\x30\x30\x30\x20\x70\x79\x74\x68\x6f\x6e\x33\x31\x31\x00\x35\xc7\x02\xe8\x50\x1e\xed\xeb\x5c\xe4\x3d\x6f\x34\x60\xd1\x1c\x79\x1c\xfe\xfa\xa7\x72\x48\xf0\x8c\xad\x55\xd0\x0c\x37\xe7\x3a\x00" ch := make(chan byte, 1000) for _, b := range []byte(treeData) { ch <- b } tree, err := parseGitTree(ch) if err != nil { t.Error(err) } found := false for _, item := range tree.items { t.Log(item) if item.name == "nodejs22" && item.hash == "873a323b262ebb3bd77b2592b2e11bdd08dbc721cbf4ac9f97637e58e1fffce7" && item.isSubmodule() { found = true break } } if !found { t.Error("expected submodule not found") } }) t.Run("parse nested trees with subtrees", func(t *testing.T) { const data = "873a323b262ebb3bd77b2592b2e11bdd08dbc721cbf4ac9f97637e58e1fffce7 tree 1083\x00100644\x20\x2Egitattributes\x00\xD8v\xA95\x87\xC1\xA9\xFCPn\xDD\xD4\x13\x9B\x8E\xD2\xCFs\xBD\x11q\x8A\xAE\x8A\x7Cg\xE2C\x14J\x01\xB0100644\x20\x2Egitignore\x00\xC3\xCD\x8En\x887\x3AJ\xA0P\xEEL\xD4\xF5\xD2v\x9C\xA6v\xC5D\x60\x40\x95\xD1\x0B\xA4\xB8\x86\xD4rE100644\x20COPYING\x00\x12\x2A\x28\xC8\xB9\x5D\x9B\x8A\x23\x1F\xE96\x07\x3F\xA9D\x90\xFD\xCE\x2Bi\x2D\x031\x5C\xCC\xC4fx\x00\xC22100644\x20README\x2Emd\x00\x92D\xF7\xFF\x0E0\x5C\xF2\xAC\x0DA\x06\x92\x0B\xD6z\x3CGh\x00y\x7EW1\xB9a\x8Ch\x215Fa100644\x20_service\x00\xC51\xF2\x12\xF3\x24\x9C\xD9\x9F\x0A\x93Mp\x12\xC1\xF7i\x05\x95\xC5Z\x06\x95i\x3Az\xC3\xF59\x7E\xF8\x1B100644\x20autogits\x2Echanges\x00\xF7\x8D\xBF\x0A\xCB\x5D\xB7y\x8C\xA9\x9C\xEB\x92\xAFd\x2C\x98\x23\x0C\x13\x13\xED\xDE\x5D\xBALD6\x3BR\x5B\xCA100644\x20autogits\x2Espec\x00\xD2\xBC\x20v\xD3\xE5F\xCA\xEE\xEA\x18\xC84\x0D\xA7\xCA\xD8O\xF2\x0A\xAB\x40\x2A\xFAL\x3B\xB4\xE6\x11\xE7o\xD140000\x20common\x00\xE2\xC9dg\xD0\x5D\xD1\xF1\x8ARW\xF0\x96\xD6\x29\x2F\x8F\xD9\xC7\x82\x1A\xB7\xAAw\xB0\xCE\xA8\xFE\xC8\xD7D\xF2100755\x20dev_test_helper\x2Esh\x00\xECY\xDD\xB3rz\x9Fh\xD4\x2E\x85\x02\x13\xF8\xFE\xB57\x8B\x1B6\x8E\x09dC\x1E\xE0\x90\x09\x08\xED\xBD_40000\x20devel\x2Dimporter\x00v\x98\x9B\x92\xD8\x24lu\xFC\xB2d\xC9\xCENb\xEE\x0F\x21\x8B\x92\x88\xDBs\xF8\x2E\xA8\xC8W\x1C\x20\xCF\xD440000\x20doc\x00\x8Akyq\xD0\xCF\xB8\x2F\x80Y\x2F\x11\xF0\x14\xA9\xFE\x96\x14\xE0W\x2C\xCF\xB9\x86\x7E\xFDi\xD7\x1F\x08Q\xFB40000\x20gitea\x2Devents\x2Drabbitmq\x2Dpublisher\x00\x5Cb\x3Fh\xA2\x06\x06\x0Cd\x09\xA5\xD9\xF7\x23\x5C\xF85\xF5\xB8\xBE\x7F\xD4O\x25t\xEF\xCC\xAB\x18\x7C\x0C\xF3100644\x20go\x2Emod\x00j\x85\x0B\x03\xC8\x9F\x9F\x0F\xC8\xE0\x8C\xF7\x3D\xC19\xF7\x12gk\xD6\x18JN\x24\xC0\x1C\xBE\x97oY\x02\x8D100644\x20go\x2Esum\x00h\x88\x2E\x27\xED\xD39\x8D\x12\x0F\x7D\x97\xA2\x5DE\xB9\x82o\x0Cu\xF4l\xA17s\x28\x2BQT\xE6\x12\x9040000\x20group\x2Dreview\x00\x7E\x7B\xB42\x0F\x3B\xC9o\x2C\xE79\x1DR\xE2\xE4i\xAE\xF6u\x90\x09\xD8\xC9c\xE7\xF7\xC7\x92\xFB\xD7\xDD140000\x20obs\x2Dstaging\x2Dbot\x00\x12\xE8\xAF\x09\xD4\x5D\x13\x8D\xC9\x0AvPDc\xB6\x7C\xAC4\xD9\xC5\xD4_\x98i\xBE2\xA7\x25aj\xE2k40000\x20obs\x2Dstatus\x2Dservice\x00MATY\xA3\xFA\xED\x05\xBE\xEB\x2B\x07\x9CN\xA9\xF3SB\x22MlV\xA4\x5D\xDA\x0B\x0F\x23\xA1\xA8z\xD740000\x20systemd\x00\x2D\xE2\x03\x7E\xBD\xEB6\x8F\xC5\x0E\x12\xD4\xBD\x97P\xDD\xA2\x92\xCE6n\x08Q\xCA\xE4\x15\x97\x8F\x26V\x3DW100644\x20vendor\x2Etar\x2Ezst\x00\xD9\x2Es\x03I\x91\x22\x24\xC86q\x91\x95\xEF\xA3\xC9\x3C\x06D\x90w\xAD\xCB\xAE\xEEu2i\xCE\x05\x09u40000\x20workflow\x2Ddirect\x00\x94\xDB\xDFc\xB5A\xD5\x16\xB3\xC3ng\x94J\xE7\x101jYF\x15Q\xE97\xCFg\x14\x12\x28\x3A\xFC\xDB40000\x20workflow\x2Dpr\x00\xC1\xD8Z9\x18\x60\xA2\xE2\xEF\xB0\xFC\xD7\x2Ah\xF07\x0D\xEC\x8A7\x7E\x1A\xAAn\x13\x9C\xEC\x05s\xE8\xBDf\x00" ch := make(chan byte, 2000) for _, b := range []byte(data) { ch <- b } tree, err := parseGitTree(ch) if err != nil { t.Error(err) } found := false for _, item := range tree.items { t.Log(item) if item.name == "workflow-pr" && item.hash == "c1d85a391860a2e2efb0fcd72a68f0370dec8a377e1aaa6e139cec0573e8bd66" && item.isTree() { found = true break } } if !found { t.Error("expected submodule not found") } }) } func TestCommitTreeParsing(t *testing.T) { gitDir := t.TempDir() testDir, _ := os.Getwd() var commitId string cmd := exec.Command("/usr/bin/bash", path.Join(testDir, "tsetup.sh")) cmd.Dir = gitDir cmd.Stdout = writeFunc(func(data []byte) (int, error) { commitId = commitId + strings.TrimSpace(string(data)) return len(data), nil }) if err := cmd.Run(); err != nil { t.Fatal(err.Error()) } gh, err := AllocateGitWorkTree(gitDir, "", "") if err != nil { t.Fatal(err) } t.Run("GitCatFile commit", func(t *testing.T) { h, _ := gh.ReadExistingPath(".") defer h.Close() file, err := h.GitCatFile("", commitId, "help") if err != nil { t.Error("failed", err) } if string(file) != "help\n" { t.Error("expected 'help\\n' but got", string(file)) } }) t.Run("GitCatFile commit", func(t *testing.T) { h, _ := gh.ReadExistingPath(".") defer h.Close() file, err := h.GitCatFile("", "HEAD", "help") if err != nil { t.Error("failed", err) } if string(file) != "help\n" { t.Error("expected 'help\\n' but got", string(file)) } }) t.Run("GitCatFile bad commit", func(t *testing.T) { h, _ := gh.ReadExistingPath(".") defer h.Close() file, err := h.GitCatFile("", "518b468f391bf01d5d76d497d7cbecfa8b46d185714cf8745800ae18afb21afd", "help") if err == nil { t.Error("expected error, but not nothing") } if string(file) != "" { t.Error("expected 'help\\n' but got", file) } }) t.Run("reads HEAD and parses the tree", func(t *testing.T) { const nodejs21 = "c678c57007d496a98bec668ae38f2c26a695f94af78012f15d044ccf066ccb41" h, _ := gh.ReadExistingPath(".") defer h.Close() id, ok := h.GitSubmoduleCommitId("", "nodejs21", commitId) if !ok { t.Error("failed parse") } if id != nodejs21 { t.Errorf("hash doesn't match: %s vs. expected %s", id, nodejs21) } }) t.Run("reads README.md", func(t *testing.T) { h, _ := gh.ReadExistingPath(".") defer h.Close() data, err := h.GitCatFile("", commitId, "README.md") if err != nil { t.Errorf("failed parse: %v", err) } if string(data) != "foo\n" || len(data) != 4 { t.Errorf("Wrong data of len: %d", len(data)) } }) t.Run("read HEAD", func(t *testing.T) { h, _ := gh.ReadExistingPath(".") defer h.Close() data, err := h.GitSubmoduleList("", "HEAD") if err != nil { t.Error("failed to get submodule list", err) } if len(data) != 5 { t.Error("Invalid len of submodules", len(data)) } }) t.Run("try to parse unknown item", func(t *testing.T) { }) } func TestGitStatusParse(t *testing.T) { testData := []struct { name string data []byte res []GitStatusData }{ { name: "Single modified line", data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00"), res: []GitStatusData{ { Path: "bots-common/git_utils.go", Status: GitStatus_Modified, }, }, }, { name: "Untracked entries", data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00? bots-common/c.out\x00? doc/Makefile\x00"), res: []GitStatusData{ { Path: "bots-common/git_utils.go", Status: GitStatus_Modified, }, { Path: "bots-common/c.out", Status: GitStatus_Untracked, }, { Path: "doc/Makefile", Status: GitStatus_Untracked, }, }, }, { name: "Untracked entries", data: []byte("1 .M N... 100644 100644 100644 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 dbe4b3d5a0a2e385f78fd41d726baa20e9190f7b5a2e78cbd4885586832f39e7 bots-common/git_utils.go\x00? bots-common/c.out\x00! doc/Makefile\x00"), res: []GitStatusData{ { Path: "bots-common/git_utils.go", Status: GitStatus_Modified, }, { Path: "bots-common/c.out", Status: GitStatus_Untracked, }, { Path: "doc/Makefile", Status: GitStatus_Ignored, }, }, }, { name: "Nothing", }, { name: "Unmerged .gitmodules during a merge", data: []byte("1 A. S... 000000 160000 160000 0000000000000000000000000000000000000000000000000000000000000000 ed07665aea0522096c88a7555f1fa9009ed0e0bac92de4613c3479516dd3d147 pkgB2\x00u UU N... 100644 100644 100644 100644 587ec403f01113f2629da538f6e14b84781f70ac59c41aeedd978ea8b1253a76 d23eb05d9ca92883ab9f4d28f3ec90c05f667f3a5c8c8e291bd65e03bac9ae3c 087b1d5f22dbf0aa4a879fff27fff03568b334c90daa5f2653f4a7961e24ea33 .gitmodules\x00"), res: []GitStatusData{ { Path: "pkgB2", Status: GitStatus_Modified, }, { Path: ".gitmodules", Status: GitStatus_Unmerged, States: [3]string{"587ec403f01113f2629da538f6e14b84781f70ac59c41aeedd978ea8b1253a76", "d23eb05d9ca92883ab9f4d28f3ec90c05f667f3a5c8c8e291bd65e03bac9ae3c", "087b1d5f22dbf0aa4a879fff27fff03568b334c90daa5f2653f4a7961e24ea33"}, SubmoduleChanges: "N...", }, }, }, { name: "Renamed file", data: []byte("1 M. N... 100644 100644 100644 d23eb05d9ca92883ab9f4d28f3ec90c05f667f3a5c8c8e291bd65e03bac9ae3c 896cd09f36d39e782d66ae32dd5614d4f4d83fc689f132aab2dfc019a9f5b6f3 .gitmodules\x002 R. S... 160000 160000 160000 3befe051a34612530acfa84c736d2454278453ec0f78ec028f25d2980f8c3559 3befe051a34612530acfa84c736d2454278453ec0f78ec028f25d2980f8c3559 R100 pkgQ\x00pkgC\x00"), res: []GitStatusData{ { Path: "pkgQ", Status: GitStatus_Renamed, States: [3]string{"pkgC"}, }, { Path: ".gitmodules", Status: GitStatus_Modified, }, }, }, } for _, test := range testData { t.Run(test.name, func(t *testing.T) { r, err := parseGitStatusData(bufio.NewReader(bytes.NewReader(test.data))) if err != nil { t.Fatal(err) } if len(r) != len(test.res) { t.Fatal("len(r):", len(r), "is not expected", len(test.res)) } for _, expected := range test.res { if !slices.Contains(r, expected) { t.Fatal("result", r, "doesn't contains expected", expected) } } }) } }