|
| 1 | +package zmaster587.advancedRocketry.test.server; |
| 2 | + |
| 3 | +import java.util.ArrayList; |
| 4 | +import java.util.List; |
| 5 | + |
| 6 | +import org.junit.Test; |
| 7 | + |
| 8 | +import static org.junit.Assert.assertEquals; |
| 9 | +import static org.junit.Assert.assertFalse; |
| 10 | +import static org.junit.Assert.assertTrue; |
| 11 | + |
| 12 | +/** |
| 13 | + * The loop the two discovery systems exist to close, driven on a real server: an address is carried |
| 14 | + * to a world, deposited there, and a tier-1 launch pad standing on that world may then be aimed at |
| 15 | + * it. |
| 16 | + * |
| 17 | + * <p>The unit and integration tiers pin the pieces - a body carries its own known-set, one body's |
| 18 | + * finds never reach its neighbour, a beacon teaches its own system. What only a server can answer is |
| 19 | + * whether the PRODUCTION gate a rocket asks agrees: `IPlanetDefiner.isPlanetKnown` reading the world |
| 20 | + * it is standing in, with the pack's authored set as the floor beneath it.</p> |
| 21 | + * |
| 22 | + * <p>Both assertions here are stated against dims the probe itself reports, never against a literal |
| 23 | + * this test wrote down: which worlds a survey resolves depends on the server's own sky.</p> |
| 24 | + * |
| 25 | + * <p>Position-isolated at x=4900-4960 (clear of the survey fixtures at 4300-4660 and the observatory |
| 26 | + * multiblock fixtures at 4000-4060).</p> |
| 27 | + */ |
| 28 | +public class Tier1AimsAtWhatThisWorldKnowsE2ETest extends AbstractSharedServerTest { |
| 29 | + |
| 30 | + private static final int CY = 64; |
| 31 | + private static final int CZ = 4900; |
| 32 | + private static final int X = 4900; |
| 33 | + |
| 34 | + private String exec(String command) throws Exception { |
| 35 | + return String.join("\n", client().execute(command)); |
| 36 | + } |
| 37 | + |
| 38 | + private String where() { |
| 39 | + return "0 " + X + " " + CY + " " + CZ; |
| 40 | + } |
| 41 | + |
| 42 | + /** Whether the gate a rocket standing in {@code standing} asks says {@code target} is known. */ |
| 43 | + private boolean known(int standing, int target) throws Exception { |
| 44 | + String reply = exec("artest planet knowledge " + standing + " " + target); |
| 45 | + assertTrue("the knowledge probe failed: " + reply, reply.contains("\"known\":")); |
| 46 | + return reply.contains("\"known\":true"); |
| 47 | + } |
| 48 | + |
| 49 | + /** The same reply's two halves, so a red test says WHICH source moved. */ |
| 50 | + private String halves(int standing, int target) throws Exception { |
| 51 | + return exec("artest planet knowledge " + standing + " " + target); |
| 52 | + } |
| 53 | + |
| 54 | + /** A numeric field of a probe reply. */ |
| 55 | + private static int intField(String json, String name) { |
| 56 | + String key = "\"" + name + "\":"; |
| 57 | + int at = json.indexOf(key); |
| 58 | + assertTrue("probe reply has no field " + name + ": " + json, at >= 0); |
| 59 | + int from = at + key.length(); |
| 60 | + int to = from; |
| 61 | + while (to < json.length() && "-0123456789".indexOf(json.charAt(to)) >= 0) { |
| 62 | + to++; |
| 63 | + } |
| 64 | + return Integer.parseInt(json.substring(from, to)); |
| 65 | + } |
| 66 | + |
| 67 | + private static List<Integer> ints(String json, String field) { |
| 68 | + String key = "\"" + field + "\":["; |
| 69 | + int at = json.indexOf(key); |
| 70 | + assertTrue("probe reply has no array " + field + ": " + json, at >= 0); |
| 71 | + int end = json.indexOf(']', at); |
| 72 | + String body = json.substring(at + key.length(), end).trim(); |
| 73 | + List<Integer> out = new ArrayList<>(); |
| 74 | + if (!body.isEmpty()) { |
| 75 | + for (String piece : body.split(",")) { |
| 76 | + out.add(Integer.parseInt(piece.trim())); |
| 77 | + } |
| 78 | + } |
| 79 | + return out; |
| 80 | + } |
| 81 | + |
| 82 | + @Test |
| 83 | + public void withResearchOnAPadIsNotOfferedAWorldNobodyHasFoundHere() throws Exception { |
| 84 | + exec("artest config set planetsMustBeDiscovered true"); |
| 85 | + |
| 86 | + assertTrue("the overworld must always be known - it is the floor every pack starts from", |
| 87 | + known(0, 0)); |
| 88 | + |
| 89 | + // The target is MINTED for this test rather than picked out of the server's planet list: a |
| 90 | + // world that has just come into existence cannot be in anybody's known-set, so the assertion |
| 91 | + // below cannot be quietly satisfied by whatever another test taught this world earlier. |
| 92 | + try { |
| 93 | + String installed = exec("artest space gen-install 0.9 2000000 987654321"); |
| 94 | + assertTrue("the procedural generator must install: " + installed, |
| 95 | + installed.contains("\"ok\":true")); |
| 96 | + String found = exec("artest space find-procedural 4"); |
| 97 | + assertTrue("a dense procedural galaxy must offer a landable body: " + found, |
| 98 | + found.contains("\"ok\":true")); |
| 99 | + String cell = intField(found, "sx") + " " + intField(found, "sy") + " " |
| 100 | + + intField(found, "sz"); |
| 101 | + String realized = exec("artest space realize " + cell); |
| 102 | + assertTrue("realization must mint a world to ask about: " + realized, |
| 103 | + realized.contains("\"ok\":true")); |
| 104 | + int fresh = intField(realized, "dim"); |
| 105 | + |
| 106 | + String reply = halves(0, fresh); |
| 107 | + assertTrue("a freshly minted world must be in nobody's global set: " + reply, |
| 108 | + reply.contains("\"global\":false")); |
| 109 | + assertTrue("nor known on the world we are standing on: " + reply, |
| 110 | + reply.contains("\"local\":false")); |
| 111 | + assertFalse("and a pad here must therefore not be offered it: " + reply, |
| 112 | + known(0, fresh)); |
| 113 | + } finally { |
| 114 | + exec("artest space gen-reset"); |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + @Test |
| 119 | + public void anAddressDepositedHereBecomesSomethingAPadHereCanBeAimedAt() throws Exception { |
| 120 | + // The sweep runs with research OFF, where what the instrument reaches is resolved outright - |
| 121 | + // the pacing is a different mechanic with its own test, and waiting for it here would only |
| 122 | + // make this fixture slower and flakier. The GATE is then asked with research ON, which is |
| 123 | + // the mode the whole question exists in. |
| 124 | + exec("artest config set planetsMustBeDiscovered false"); |
| 125 | + exec("artest config set telescopeLimitingMagnitude 30"); |
| 126 | + exec("artest config set telescopePassiveRadiusSteps 1"); |
| 127 | + |
| 128 | + String placed = exec("artest telescope place " + where()); |
| 129 | + assertTrue("could not place an observatory: " + placed, placed.contains("\"ok\":true")); |
| 130 | + String crystal = exec("artest telescope crystal " + where()); |
| 131 | + assertTrue("the crystal must start blank: " + crystal, crystal.contains("\"addresses\":0")); |
| 132 | + |
| 133 | + // The instrument watching its own neighbourhood: what it resolves are the bodies of the |
| 134 | + // system this observatory is standing in, which are the ones that have worlds to fly to. |
| 135 | + String swept = exec("artest telescope passive " + where()); |
| 136 | + assertTrue("the passive sweep did not start: " + swept, swept.contains("\"ok\":true")); |
| 137 | + String afterSweep = exec("artest telescope info " + where()); |
| 138 | + assertFalse("the sweep must be finished with research off: " + afterSweep, |
| 139 | + afterSweep.contains("\"scanning\":true")); |
| 140 | + exec("artest config set planetsMustBeDiscovered true"); |
| 141 | + |
| 142 | + String deposited = exec("artest telescope deposit " + where()); |
| 143 | + List<Integer> landed = ints(deposited, "dims"); |
| 144 | + assertFalse("the sweep resolved nothing with a world in it, so there is nothing to deposit" |
| 145 | + + " and this fixture proves nothing: " + deposited, landed.isEmpty()); |
| 146 | + |
| 147 | + for (int dim : landed) { |
| 148 | + String reply = halves(0, dim); |
| 149 | + assertTrue("a deposited address must be known to a pad standing here: " + reply, |
| 150 | + reply.contains("\"known\":true")); |
| 151 | + assertTrue("and it must be known LOCALLY - the deposit may not touch the global floor: " |
| 152 | + + reply, reply.contains("\"local\":true")); |
| 153 | + } |
| 154 | + |
| 155 | + String depositedAgain = exec("artest telescope deposit " + where()); |
| 156 | + assertEquals("depositing the same crystal twice must land the same addresses, not more", |
| 157 | + landed, ints(depositedAgain, "dims")); |
| 158 | + } |
| 159 | +} |
0 commit comments