package e2e import ( "net/http" "os/exec" "strings" "testing" ) // Chapter 14: a malformed config falls back to the last version that parsed, not to the defaults. func TestABrokenConfigKeepsTheSettingsThatWorked(t *testing.T) { if _, err := exec.LookPath("git"); err != nil { t.Skip("git is not installed") } in := newInstance(t) john := in.account("john") lisa := in.account("lisa") work := seed(t, in, john, "john", "johnbot") write(t, work, ".barerepo/config", "[repo]\nvisibility = \"public\"\n\n[access]\npush = [\"john\", \"lisa\"]\n") run(t, work, "git", "add", "-A") run(t, work, "git", "commit", "-qm", "open it up and let lisa push") run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "master") // A stranger can read it and lisa can push, which is what the good file bought. if code, _, _ := get(t, in.http.URL+"/john/johnbot"); code != http.StatusOK { t.Fatalf("the repository is not public yet, so the test proves nothing later") } // One unclosed bracket, which is the shape of every real config typo. write(t, work, ".barerepo/config", "[repo\nvisibility = \"public\"\n") run(t, work, "git", "add", "-A") run(t, work, "git", "commit", "-qm", "a typo in the config") out := run(t, work, "git", "push", in.url(john, "/john/johnbot"), "master") // The push that makes the typo is the one that reports it, not whoever pushes next. if !strings.Contains(out, "does not parse") { t.Errorf("the push that broke the file said nothing about it:\n%s", out) } // And every later push says which older version is doing the deciding. write(t, work, "conn.go", "package irc\n") run(t, work, "git", "add", "-A") run(t, work, "git", "commit", "-qm", "an unrelated change") later := run(t, work, "git", "push", in.url(john, "/john/johnbot"), "master") if !strings.Contains(later, "does not parse") { t.Errorf("a later push said nothing about the broken file:\n%s", later) } if !strings.Contains(later, "still in force") { t.Errorf("a later push did not name the settings that are deciding:\n%s", later) } // The defaults are private, so a public repository is proof the old file is still being read. if code, _, _ := get(t, in.http.URL+"/john/johnbot"); code != http.StatusOK { t.Errorf("a typo in the config took the repository private, and chapter 14 forbids that") } // And the access list survived, which is the clause about not locking anyone out. mineDir := clone(t, in, lisa, "/john/johnbot") commit(t, mineDir, "package main\n\nfunc main() {}\n", "lisa can still push") if _, err := try(t, mineDir, "git", "push", in.url(lisa, "/john/johnbot"), "master"); err != nil { t.Errorf("lisa lost her push access to a typo: %v", err) } // A reader of the config page is reading the broken file and must be told it is not in force. code, _, body := get(t, in.http.URL+"/john/johnbot/config") if code != http.StatusOK { t.Fatalf("the config page answered %d", code) } if !strings.Contains(body, "does not parse") { t.Errorf("the config page shows the broken file as though it were in force:\n%s", body) } }