github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/scm/fetcher_test.go (about)

     1  package scm_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/quickfeed/quickfeed/kit/sh"
    13  	"github.com/quickfeed/quickfeed/qf"
    14  	"github.com/quickfeed/quickfeed/scm"
    15  )
    16  
    17  func TestClone(t *testing.T) {
    18  	qfTestOrg := scm.GetTestOrganization(t)
    19  	s, userName := scm.GetTestSCM(t)
    20  
    21  	ctx := context.Background()
    22  	dstDir := t.TempDir()
    23  	assignmentDir, err := s.Clone(ctx, &scm.CloneOptions{
    24  		Organization: qfTestOrg,
    25  		Repository:   qf.StudentRepoName(userName),
    26  		DestDir:      dstDir,
    27  	})
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	testsDir, err := s.Clone(ctx, &scm.CloneOptions{
    32  		Organization: qfTestOrg,
    33  		Repository:   qf.TestsRepo,
    34  		DestDir:      dstDir,
    35  	})
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  	// Note: the following depends on the actual content of
    40  	// the <student>-labs and tests repositories of the qfTestOrg.
    41  	found, err := exists(filepath.Join(assignmentDir, "lab1"))
    42  	if !found {
    43  		t.Fatalf("lab1 not found in 'assignments': %v", err)
    44  	}
    45  	found, err = exists(filepath.Join(assignmentDir, "lab2"))
    46  	if found {
    47  		t.Fatalf("lab2 found in 'assignments' unexpectedly: %v", err)
    48  	}
    49  	expectedTestsDirs := []string{
    50  		"lab1",
    51  		"lab2",
    52  		"lab3",
    53  		"lab4",
    54  		"lab5",
    55  		"lab6",
    56  		"scripts",
    57  	}
    58  	for _, dir := range expectedTestsDirs {
    59  		found, err = exists(filepath.Join(testsDir, dir))
    60  		if !found {
    61  			t.Errorf("%s not found in 'tests': %v", dir, err)
    62  		}
    63  	}
    64  }
    65  
    66  func appendToFile(filename, text string) (err error) {
    67  	f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	defer func() {
    72  		closeErr := f.Close()
    73  		if err == nil {
    74  			err = closeErr
    75  		}
    76  	}()
    77  	_, err = f.WriteString(text)
    78  	return
    79  }
    80  
    81  // Test that we can clone a repository, update it (commit and push) and clone it again twice.
    82  // The two last clones are in a different directory.
    83  // The third clone is actually a fast-forward pull.
    84  func TestCloneTwice(t *testing.T) {
    85  	qfTestOrg := scm.GetTestOrganization(t)
    86  	s, _ := scm.GetTestSCM(t)
    87  
    88  	ctx := context.Background()
    89  	dstDir := t.TempDir()
    90  
    91  	testsDir, err := s.Clone(ctx, &scm.CloneOptions{
    92  		Organization: qfTestOrg,
    93  		Repository:   qf.TestsRepo,
    94  		DestDir:      dstDir,
    95  	})
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	if found, err := exists(testsDir); !found {
   100  		t.Fatalf("%s not found: %v", testsDir, err)
   101  	}
   102  	twiceMsg := fmt.Sprintf("Update tests repo %s\n", time.Now().Format(time.Kitchen))
   103  	if err := appendToFile(filepath.Join(testsDir, "README.md"), twiceMsg); err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	commitMsg := fmt.Sprintf("Clone twice commit %s", time.Now().Format(time.Kitchen))
   107  	if err := sh.RunA("git", "-C", testsDir, "commit", "-a", "-m", commitMsg); err != nil {
   108  		t.Fatal(err)
   109  	}
   110  	if err := sh.RunA("git", "-C", testsDir, "push"); err != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	// Clone to a new directory to ensure that we get a new clone with the change we just made.
   115  	dstDir = t.TempDir()
   116  
   117  	testsDir, err = s.Clone(ctx, &scm.CloneOptions{
   118  		Organization: qfTestOrg,
   119  		Repository:   qf.TestsRepo,
   120  		DestDir:      dstDir,
   121  	})
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  	b, err := os.ReadFile(filepath.Join(testsDir, "README.md"))
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if !strings.Contains(string(b), twiceMsg[:len(twiceMsg)-2]) {
   130  		t.Fatalf("README.md does not contain %q", twiceMsg)
   131  	}
   132  
   133  	// Clone to the same directory to test that we get a fast-forward pull.
   134  	testsDir, err = s.Clone(ctx, &scm.CloneOptions{
   135  		Organization: qfTestOrg,
   136  		Repository:   qf.TestsRepo,
   137  		DestDir:      dstDir,
   138  	})
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	b, err = os.ReadFile(filepath.Join(testsDir, "README.md"))
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	if !strings.Contains(string(b), twiceMsg[:len(twiceMsg)-2]) {
   147  		t.Fatalf("README.md does not contain %q", twiceMsg)
   148  	}
   149  }
   150  
   151  func TestCloneBranch(t *testing.T) {
   152  	qfTestOrg := scm.GetTestOrganization(t)
   153  	s, userName := scm.GetTestSCM(t)
   154  
   155  	ctx := context.Background()
   156  	dstDir := t.TempDir()
   157  	assignmentDir, err := s.Clone(ctx, &scm.CloneOptions{
   158  		Organization: qfTestOrg,
   159  		Repository:   qf.StudentRepoName(userName),
   160  		DestDir:      dstDir,
   161  		Branch:       "hotfix",
   162  	})
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	// Note: the following depends on the actual existence of the lab2
   167  	// folder in the <student>-labs repository of the qfTestOrg.
   168  	found, err := exists(filepath.Join(assignmentDir, "lab2"))
   169  	if !found {
   170  		t.Fatalf("lab2 not found in %s: %v", assignmentDir, err)
   171  	}
   172  }
   173  
   174  // exists returns whether the given file or directory exists
   175  func exists(path string) (bool, error) {
   176  	_, err := os.Stat(path)
   177  	if err == nil {
   178  		return true, nil
   179  	}
   180  	if os.IsNotExist(err) {
   181  		return false, nil
   182  	}
   183  	return false, err
   184  }