View Javadoc
1   /*
2    * Copyright (C) 2022, Matthias Fromme <mfromme@dspace.de>
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.lfs;
11  
12  import static org.junit.Assert.assertEquals;
13  import static org.junit.Assert.assertTrue;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.OutputStream;
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.eclipse.jgit.api.Git;
23  import org.eclipse.jgit.api.ResetCommand.ResetType;
24  import org.eclipse.jgit.attributes.FilterCommand;
25  import org.eclipse.jgit.attributes.FilterCommandRegistry;
26  import org.eclipse.jgit.junit.RepositoryTestCase;
27  import org.eclipse.jgit.lfs.internal.LfsConnectionFactory;
28  import org.eclipse.jgit.lfs.lib.Constants;
29  import org.eclipse.jgit.lib.ConfigConstants;
30  import org.eclipse.jgit.lib.Repository;
31  import org.eclipse.jgit.lib.StoredConfig;
32  import org.eclipse.jgit.transport.http.HttpConnection;
33  import org.eclipse.jgit.util.HttpSupport;
34  import org.junit.AfterClass;
35  import org.junit.Before;
36  import org.junit.BeforeClass;
37  import org.junit.Test;
38  
39  /**
40   * Test if the lfs config is used in the correct way during checkout.
41   *
42   * Two lfs-files are created, one that comes before .gitattributes and
43   * .lfsconfig in git order (".aaa.txt") and one that comes after ("zzz.txt").
44   *
45   * During checkout/reset it is tested if the correct version of the lfs config
46   * is used.
47   *
48   * TODO: The current behavior seems a little bit strange/unintuitive. Some files
49   * are checked out before and some after the config files. This leads to the
50   * behavior, that during a single command the config changes. Since this seems
51   * to be the same way in native git, the behavior is accepted for now.
52   *
53   */
54  public class LfsConfigGitTest extends RepositoryTestCase {
55  
56  	private static final String SMUDGE_NAME = org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
57  			+ Constants.ATTR_FILTER_DRIVER_PREFIX
58  			+ org.eclipse.jgit.lib.Constants.ATTR_FILTER_TYPE_SMUDGE;
59  
60  	private static final String LFS_SERVER_URI1 = "https://lfs.server1/test/uri";
61  
62  	private static final String EXPECTED_SERVER_URL1 = LFS_SERVER_URI1
63  			+ Protocol.OBJECTS_LFS_ENDPOINT;
64  
65  	private static final String LFS_SERVER_URI2 = "https://lfs.server2/test/uri";
66  
67  	private static final String EXPECTED_SERVER_URL2 = LFS_SERVER_URI2
68  			+ Protocol.OBJECTS_LFS_ENDPOINT;
69  
70  	private static final String LFS_SERVER_URI3 = "https://lfs.server3/test/uri";
71  
72  	private static final String EXPECTED_SERVER_URL3 = LFS_SERVER_URI3
73  			+ Protocol.OBJECTS_LFS_ENDPOINT;
74  
75  	private static final String FAKE_LFS_POINTER1 = "version https://git-lfs.github.com/spec/v1\n"
76  			+ "oid sha256:6ce9fab52ee9a6c4c097def4e049c6acdeba44c99d26e83ba80adec1473c9b2d\n"
77  			+ "size 253952\n";
78  
79  	private static final String FAKE_LFS_POINTER2 = "version https://git-lfs.github.com/spec/v1\n"
80  			+ "oid sha256:a4b711cd989863ae2038758a62672138347abbbae4076a7ad3a545fda7d08f82\n"
81  			+ "size 67072\n";
82  
83  	private static List<String> checkoutURLs = new ArrayList<>();
84  
85  	static class SmudgeFilterMock extends FilterCommand {
86  		public SmudgeFilterMock(Repository db, InputStream in,
87  				OutputStream out) throws IOException {
88  			super(in, out);
89  			HttpConnection lfsServerConn = LfsConnectionFactory.getLfsConnection(db,
90  					HttpSupport.METHOD_POST, Protocol.OPERATION_DOWNLOAD);
91  			checkoutURLs.add(lfsServerConn.getURL().toString());
92  		}
93  
94  		@Override
95  		public int run() throws IOException {
96  			// Stupid no impl
97  			in.transferTo(out);
98  			return -1;
99  		}
100 	}
101 
102 	@BeforeClass
103 	public static void installLfs() {
104 		FilterCommandRegistry.register(SMUDGE_NAME, SmudgeFilterMock::new);
105 	}
106 
107 	@AfterClass
108 	public static void removeLfs() {
109 		FilterCommandRegistry.unregister(SMUDGE_NAME);
110 	}
111 
112 	private Git git;
113 
114 	@Override
115 	@Before
116 	public void setUp() throws Exception {
117 		super.setUp();
118 		git = new Git(db);
119 		// commit something
120 		writeTrashFile("Test.txt", "Hello world");
121 		git.add().addFilepattern("Test.txt").call();
122 		git.commit().setMessage("Initial commit").call();
123 		// prepare the config for LFS
124 		StoredConfig config = git.getRepository().getConfig();
125 		config.setString("filter", "lfs", "smudge", SMUDGE_NAME);
126 		config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
127 				ConfigConstants.CONFIG_KEY_AUTOCRLF, "false");
128 		config.save();
129 
130 		fileBefore = null;
131 		fileAfter = null;
132 		configFile = null;
133 		gitAttributesFile = null;
134 	}
135 
136 	File fileBefore;
137 
138 	File fileAfter;
139 
140 	File configFile;
141 
142 	File gitAttributesFile;
143 
144 	private void createLfsFiles(String lfsPointer) throws Exception {
145 		//File to be checked out before lfs config
146 		String fileNameBefore = ".aaa.txt";
147 		fileBefore = writeTrashFile(fileNameBefore, lfsPointer);
148 		git.add().addFilepattern(fileNameBefore).call();
149 
150 		// File to be checked out after lfs config
151 		String fileNameAfter = "zzz.txt";
152 		fileAfter = writeTrashFile(fileNameAfter, lfsPointer);
153 		git.add().addFilepattern(fileNameAfter).call();
154 
155 		git.commit().setMessage("Commit LFS Pointer files").call();
156 	}
157 
158 
159 	private String addLfsConfigFiles(String lfsServerUrl) throws Exception {
160 		// Add config files to the repo
161 		String lfsConfig1 = createLfsConfig(lfsServerUrl);
162 		git.add().addFilepattern(Constants.DOT_LFS_CONFIG).call();
163 		// Modify gitattributes on second call, to force checkout too.
164 		if (gitAttributesFile == null) {
165 			gitAttributesFile = writeTrashFile(".gitattributes",
166 				"*.txt filter=lfs");
167 		} else {
168 			gitAttributesFile = writeTrashFile(".gitattributes",
169 					"*.txt filter=lfs\n");
170 		}
171 
172 		git.add().addFilepattern(".gitattributes").call();
173 		git.commit().setMessage("Commit config files").call();
174 		return lfsConfig1;
175 	}
176 
177 	private String createLfsConfig(String lfsServerUrl) throws IOException {
178 		String lfsConfig1 = "[lfs]\n    url = " + lfsServerUrl;
179 		configFile = writeTrashFile(Constants.DOT_LFS_CONFIG, lfsConfig1);
180 		return lfsConfig1;
181 	}
182 
183 	@Test
184 	public void checkoutLfsObjects_reset() throws Exception {
185 		createLfsFiles(FAKE_LFS_POINTER1);
186 		String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1);
187 
188 		// Delete files to force action on reset
189 		assertTrue(configFile.delete());
190 		assertTrue(fileBefore.delete());
191 		assertTrue(fileAfter.delete());
192 
193 		assertTrue(gitAttributesFile.delete());
194 
195 		// create config file with different url
196 		createLfsConfig(LFS_SERVER_URI3);
197 
198 		checkoutURLs.clear();
199 		git.reset().setMode(ResetType.HARD).call();
200 
201 		checkFile(configFile, lfsConfig1);
202 		checkFile(fileBefore, FAKE_LFS_POINTER1);
203 		checkFile(fileAfter, FAKE_LFS_POINTER1);
204 
205 		assertEquals(2, checkoutURLs.size());
206 		// TODO: Should may be EXPECTED_SERVR_URL1
207 		assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0));
208 		assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1));
209 	}
210 
211 	@Test
212 	public void checkoutLfsObjects_BranchSwitch() throws Exception {
213 		// Create a new branch "URL1" and add config files
214 		git.checkout().setCreateBranch(true).setName("URL1").call();
215 
216 		createLfsFiles(FAKE_LFS_POINTER1);
217 		String lfsConfig1 = addLfsConfigFiles(LFS_SERVER_URI1);
218 
219 		// Create a second new branch "URL2" and add config files
220 		git.checkout().setCreateBranch(true).setName("URL2").call();
221 
222 		createLfsFiles(FAKE_LFS_POINTER2);
223 		String lfsConfig2 = addLfsConfigFiles(LFS_SERVER_URI2);
224 
225 		checkFile(configFile, lfsConfig2);
226 		checkFile(fileBefore, FAKE_LFS_POINTER2);
227 		checkFile(fileAfter, FAKE_LFS_POINTER2);
228 
229 		checkoutURLs.clear();
230 		git.checkout().setName("URL1").call();
231 
232 		checkFile(configFile, lfsConfig1);
233 		checkFile(fileBefore, FAKE_LFS_POINTER1);
234 		checkFile(fileAfter, FAKE_LFS_POINTER1);
235 
236 		assertEquals(2, checkoutURLs.size());
237 		// TODO: Should may be EXPECTED_SERVR_URL1
238 		assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(0));
239 		assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(1));
240 
241 		checkoutURLs.clear();
242 		git.checkout().setName("URL2").call();
243 
244 		checkFile(configFile, lfsConfig2);
245 		checkFile(fileBefore, FAKE_LFS_POINTER2);
246 		checkFile(fileAfter, FAKE_LFS_POINTER2);
247 
248 		assertEquals(2, checkoutURLs.size());
249 		// TODO: Should may be EXPECTED_SERVR_URL2
250 		assertEquals(EXPECTED_SERVER_URL1, checkoutURLs.get(0));
251 		assertEquals(EXPECTED_SERVER_URL2, checkoutURLs.get(1));
252 	}
253 
254 	@Test
255 	public void checkoutLfsObjects_BranchSwitch_ModifiedLocal()
256 			throws Exception {
257 
258 		// Create a new branch "URL1" and add config files
259 		git.checkout().setCreateBranch(true).setName("URL1").call();
260 
261 		createLfsFiles(FAKE_LFS_POINTER1);
262 		addLfsConfigFiles(LFS_SERVER_URI1);
263 
264 		// Create a second new branch "URL2" and add config files
265 		git.checkout().setCreateBranch(true).setName("URL2").call();
266 
267 		createLfsFiles(FAKE_LFS_POINTER2);
268 		addLfsConfigFiles(LFS_SERVER_URI1);
269 
270 		// create config file with different url
271 		assertTrue(configFile.delete());
272 		String lfsConfig3 = createLfsConfig(LFS_SERVER_URI3);
273 
274 		checkFile(configFile, lfsConfig3);
275 		checkFile(fileBefore, FAKE_LFS_POINTER2);
276 		checkFile(fileAfter, FAKE_LFS_POINTER2);
277 
278 		checkoutURLs.clear();
279 		git.checkout().setName("URL1").call();
280 
281 		checkFile(fileBefore, FAKE_LFS_POINTER1);
282 		checkFile(fileAfter, FAKE_LFS_POINTER1);
283 		checkFile(configFile, lfsConfig3);
284 
285 		assertEquals(2, checkoutURLs.size());
286 
287 		assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0));
288 		assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1));
289 
290 		checkoutURLs.clear();
291 		git.checkout().setName("URL2").call();
292 
293 		checkFile(fileBefore, FAKE_LFS_POINTER2);
294 		checkFile(fileAfter, FAKE_LFS_POINTER2);
295 		checkFile(configFile, lfsConfig3);
296 
297 		assertEquals(2, checkoutURLs.size());
298 		assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(0));
299 		assertEquals(EXPECTED_SERVER_URL3, checkoutURLs.get(1));
300 	}
301 }