Microservices_Testing / index.html
eaglelandsonce's picture
Update index.html
5a84604 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Microservices Testing — TicTacToe</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gradient-to-br from-indigo-50 to-sky-100 min-h-screen flex items-center justify-center p-6">
<div class="w-full max-w-5xl bg-white rounded-2xl shadow-xl p-6">
<h1 class="text-3xl md:text-4xl font-extrabold text-center text-indigo-700">Microservices Testing — TicTacToe</h1>
<p class="text-center text-sm text-gray-600 mt-2">
Source:
<a class="text-blue-600 underline" target="_blank" rel="noopener"
href="https://www.linkedin.com/pulse/automated-testing-microservices-from-concept-practice-michael-lively-gamte/">
Automated Testing in Microservices: From Concept to Practice — Michael Lively
</a>
</p>
<div id="banner" class="hidden bg-indigo-100 border border-indigo-300 text-indigo-900 font-semibold text-center py-2 rounded mt-4"></div>
<div class="mt-6 flex flex-col md:flex-row md:space-x-6 space-y-4 md:space-y-0">
<!-- Board -->
<div id="board" class="grid grid-cols-3 gap-2 p-2 bg-gray-50 rounded-xl border-4 border-gray-200 mx-auto"></div>
<!-- Question Panel -->
<div id="questionPanel" class="md:w-1/2 w-full bg-gray-50 rounded-xl shadow-inner p-4">
<h2 id="panelQuestion" class="text-lg md:text-xl font-semibold text-gray-800">
Select a square to view the question
</h2>
<ul id="panelChoices" class="mt-4 space-y-2"></ul>
<p id="panelHint" class="mt-3 text-sm text-gray-600 hidden"></p>
<div class="mt-4 flex items-center space-x-2">
<button id="hintBtn" class="px-3 py-1.5 bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Show Hint</button>
<button id="clearBtn" class="px-3 py-1.5 bg-white border rounded hover:bg-gray-100">Clear Panel</button>
</div>
</div>
</div>
<div class="flex flex-col md:flex-row md:items-center md:justify-between mt-6 space-y-3 md:space-y-0">
<p id="status" class="text-lg font-medium text-gray-800"></p>
<div class="flex items-center space-x-2">
<label class="text-sm text-gray-600">Mode:</label>
<select id="mode" class="border rounded px-2 py-1 text-sm">
<option value="two">Two Players (X vs O)</option>
<option value="solo">Solo (You are X)</option>
</select>
<button id="restartBtn" class="px-4 py-2 bg-indigo-700 text-white rounded hover:bg-indigo-800">Restart</button>
</div>
</div>
</div>
<script>
// --- Utilities ---
const randShuffle = (arr) => arr
.map(x => ({ x, r: Math.random() }))
.sort((a,b) => a.r - b.r)
.map(o => o.x);
// --- Question bank (answers identified by 'answer' index BEFORE shuffle) ---
// Built from your provided content: automation benefits, test layers, CI/CD, tools, trends, and best practices.
const QUESTION_BANK = [
{
question: 'Why is manual testing insufficient for microservices at scale?',
choices: [
'Microservices never change once deployed',
'Manual testing is slow, error-prone, and can’t keep pace with highly distributed systems',
'Because UIs don’t need testing',
'It costs more than writing the entire system again'
],
answer: 2,
hint: 'Think speed + distribution + repeatability.'
},
{
question: 'A core benefit of automated testing is that tests are…',
choices: ['Randomized', 'Deterministic and reproducible', 'UI-only', 'Manual by default'],
answer: 2,
hint: 'Same inputs → same results.'
},
{
question: 'Which layer validates a single small unit like a function or method?',
choices: ['Integration testing', 'End-to-end testing', 'Unit testing', 'Contract testing'],
answer: 3,
hint: 'Foundation of the pyramid.'
},
{
question: 'Which testing ensures services communicate correctly together?',
choices: ['Integration testing', 'Accessibility testing', 'Static analysis', 'Exploratory testing'],
answer: 1,
hint: 'Service-to-service behavior.'
},
{
question: 'Which approach guarantees a service still fulfills expectations when it changes?',
choices: ['Contract testing', 'Chaos testing', 'Snapshot testing', 'Golden master testing'],
answer: 1,
hint: 'Inputs/outputs pact.'
},
{
question: 'Which is best described as simulating user interactions across the system, including dependencies?',
choices: ['Functional testing', 'Linting', 'Schema migration testing', 'Fuzzing only'],
answer: 1,
hint: 'User-focused workflows.'
},
{
question: 'Black-box validation of the whole architecture against business goals after deployment is…',
choices: ['End-to-end testing', 'Mutation testing', 'Smoke testing', 'Code coverage'],
answer: 1,
hint: 'System-level validation.'
},
{
question: 'Where does automation fit in a modern pipeline?',
choices: [
'Only after production',
'Embedded in CI/CD with automated gates and rapid feedback',
'Only on developer laptops',
'Only in long quarterly test cycles'
],
answer: 2,
hint: 'Shift earlier, fail faster.'
},
{
question: 'Which tool is commonly used for load/performance testing in microservices?',
choices: ['Selenium', 'Apache JMeter or Gatling', 'Pa11y', 'Axe-core'],
answer: 2,
hint: 'Think throughput and latency.'
},
{
question: 'Which tool focuses on distributed tracing for service interactions?',
choices: ['Jaeger', 'Pact', 'Hoverfly', 'Testcontainers'],
answer: 1,
hint: 'Trace requests across services.'
},
{
question: 'Which tool supports contract testing for APIs and messaging?',
choices: ['Pact', 'Jaeger', 'Axe-core', 'Katalon'],
answer: 1,
hint: 'Consumer–provider agreements.'
},
{
question: 'Which helps create ephemeral, repeatable dependencies (DBs, queues) for tests via containers?',
choices: ['Hoverfly', 'Testcontainers', 'Selenium Grid', 'Pa11y'],
answer: 2,
hint: 'Spin up what you need on demand.'
},
{
question: 'Which helps with service virtualization and integration testing when dependencies are unavailable?',
choices: ['Hoverfly', 'SBOM', 'Gitleaks', 'Snyk Code'],
answer: 1,
hint: 'Simulate services.'
},
{
question: 'Which category accelerates test creation without heavy scripting?',
choices: ['Codeless platforms (e.g., ACCELQ, Katalon)', 'Shell scripts only', 'Makefiles only', 'SQL migrations'],
answer: 1,
hint: 'Bridge dev and non-dev testers.'
},
{
question: 'AI-driven testing in 2025 commonly does what?',
choices: [
'Ignores failing tests',
'Prioritizes cases, generates scripts, and self-heals tests',
'Removes CI entirely',
'Eliminates the need for version control'
],
answer: 2,
hint: 'Agentic assist + resilience.'
},
{
question: 'Which supports accessibility automation against WCAG?',
choices: ['Axe / Pa11y', 'curl / jq', 'Terraform', 'Prometheus'],
answer: 1,
hint: 'Inclusive UX checks.'
},
{
question: 'Shift-left security in pipelines commonly includes:',
choices: [
'SAST/DAST, SBOM generation, and image signing',
'Only manual penetration tests',
'Turning off TLS for speed',
'Ignoring dependency vulnerabilities'
],
answer: 1,
hint: 'Security as code, early.'
},
{
question: 'Why use ephemeral environments for testing?',
choices: [
'They are slower but cheaper',
'They ensure clean states per run and avoid “works on my machine”',
'They replace production entirely',
'They require manual setup every time'
],
answer: 2,
hint: 'Spin up, test, tear down.'
},
{
question: 'A key best practice to avoid tight coupling in tests is to…',
choices: [
'Always test with every real dependency',
'Mock or virtualize dependencies when appropriate',
'Skip integration tests',
'Use only UI tests'
],
answer: 2,
hint: 'Minimize brittle cross-service reliance.'
},
{
question: 'Prioritization guidance in complex systems suggests testing…',
choices: [
'Low-value edge features first',
'Only the UI',
'High-risk, business-critical services first',
'Only the database layer'
],
answer: 3,
hint: 'Risk-driven focus.'
},
{
question: 'Which tool pairing is most appropriate: performance metrics store?',
choices: ['InfluxDB', 'Selenium IDE', 'Kibana only', 'Cypress'],
answer: 1,
hint: 'Time-series for perf data.'
},
{
question: 'Functional automation frameworks like Selenium are strongest for…',
choices: ['API contract validation', 'UI interaction simulation', 'Tracing spans', 'Generating SBOMs'],
answer: 2,
hint: 'Clicks, forms, flows.'
},
{
question: 'Scalable fuzzing in microservices helps by…',
choices: [
'Generating random inputs to uncover edge cases across services',
'Replacing unit tests',
'Eliminating the need for logs',
'Forcing 100% code coverage automatically'
],
answer: 1,
hint: 'Probe the unknowns.'
},
{
question: 'In CI, a typical sequence is:',
choices: [
'Deploy to prod → write tests → run tests',
'Build → test (unit/integration/contract) → gate → stage deploy',
'Write docs → ship',
'Only manual QA after release'
],
answer: 2,
hint: 'Feedback before promotion.'
}
];
// --- Game State ---
let currentPlayer, boardState, selectedCell, cellQuestions, mode;
const boardEl = document.getElementById('board');
const statusEl = document.getElementById('status');
const bannerEl = document.getElementById('banner');
const hintBtn = document.getElementById('hintBtn');
const clearBtn = document.getElementById('clearBtn');
const panelQuestion = document.getElementById('panelQuestion');
const panelChoices = document.getElementById('panelChoices');
const panelHint = document.getElementById('panelHint');
const restartBtn = document.getElementById('restartBtn');
const modeSel = document.getElementById('mode');
function initGame() {
mode = modeSel.value;
currentPlayer = 'X';
boardState = Array(9).fill('');
bannerEl.classList.add('hidden');
statusEl.innerText = '';
selectedCell = null;
// Pick 9 questions at random, then for each, shuffle choices and remap the correct answer index
const picked = randShuffle(QUESTION_BANK).slice(0, 9).map(q => {
const original = q.choices.map((c, i) => ({ text: c, idx: i + 1 }));
const shuffled = randShuffle(original);
const newChoices = shuffled.map(o => o.text);
const newAnswerIndex = shuffled.findIndex(o => o.idx === q.answer) + 1;
return {
question: q.question,
choices: newChoices,
answer: newAnswerIndex,
hint: q.hint || ''
};
});
cellQuestions = picked;
panelQuestion.innerText = 'Select a square to view the question';
panelChoices.innerHTML = '';
panelHint.classList.add('hidden');
panelHint.innerText = '';
renderBoard();
}
function renderBoard() {
boardEl.innerHTML = '';
boardState.forEach((mark, idx) => {
const btn = document.createElement('button');
let cls = 'bg-white h-24 w-24 md:h-28 md:w-28 flex items-center justify-center text-3xl font-extrabold rounded-xl shadow hover:bg-gray-100 transition';
if (mark === 'X') cls += ' text-indigo-700';
if (mark === 'O') cls += ' text-sky-700';
btn.className = cls;
btn.innerText = mark;
btn.disabled = mark !== '';
btn.addEventListener('click', () => openQuestion(idx));
boardEl.appendChild(btn);
});
statusEl.innerText = `Current: ${currentPlayer}`;
}
function openQuestion(idx) {
selectedCell = idx;
const q = cellQuestions[idx];
panelQuestion.innerText = q.question;
panelChoices.innerHTML = '';
panelHint.classList.add('hidden');
panelHint.innerText = q.hint || '';
q.choices.forEach((c, i) => {
const li = document.createElement('li');
const btn = document.createElement('button');
btn.innerText = c;
btn.className = 'w-full text-left px-4 py-2 bg-gray-100 rounded hover:bg-gray-200';
btn.addEventListener('click', () => handleAnswer(i + 1));
li.appendChild(btn);
panelChoices.appendChild(li);
});
}
function handleAnswer(choice) {
const q = cellQuestions[selectedCell];
if (choice === q.answer) {
boardState[selectedCell] = currentPlayer;
renderBoard();
if (checkWin(currentPlayer)) return endGame(`${currentPlayer} wins!`);
if (boardState.every(c => c)) return endGame('Stalemate!');
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
statusEl.innerText = `Current: ${currentPlayer}`;
if (mode === 'solo' && currentPlayer === 'O') {
setTimeout(aiMove, 450);
}
} else {
alert('Incorrect! Turn missed.');
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
statusEl.innerText = `Current: ${currentPlayer}`;
if (mode === 'solo' && currentPlayer === 'O') {
setTimeout(aiMove, 450);
}
}
}
function checkWin(player) {
const wins = [
[0,1,2],[3,4,5],[6,7,8],
[0,3,6],[1,4,7],[2,5,8],
[0,4,8],[2,4,6]
];
return wins.some(combo => combo.every(i => boardState[i] === player));
}
function endGame(msg) {
bannerEl.innerText = `🎉 ${msg}`;
bannerEl.classList.remove('hidden');
document.querySelectorAll('#board button').forEach(b => b.disabled = true);
}
// Simple AI: pick a random open square; 55% chance to "answer" correctly.
function aiMove() {
const open = boardState.map((v,i) => v === '' ? i : null).filter(v => v !== null);
if (open.length === 0) return;
const pick = open[Math.floor(Math.random() * open.length)];
const q = cellQuestions[pick];
const aiCorrect = Math.random() < 0.55;
if (aiCorrect) {
boardState[pick] = 'O';
renderBoard();
if (checkWin('O')) return endGame('O wins!');
if (boardState.every(c => c)) return endGame('Stalemate!');
currentPlayer = 'X';
statusEl.innerText = `Current: ${currentPlayer}`;
} else {
currentPlayer = 'X';
statusEl.innerText = `Current: ${currentPlayer}`;
}
}
hintBtn.addEventListener('click', () => panelHint.classList.toggle('hidden'));
clearBtn.addEventListener('click', () => {
panelQuestion.innerText = 'Select a square to view the question';
panelChoices.innerHTML = '';
panelHint.classList.add('hidden');
panelHint.innerText = '';
});
restartBtn.addEventListener('click', initGame);
modeSel.addEventListener('change', initGame);
// Start
initGame();
</script>
</body>
</html>