main
John Lauer Publish 0.2.49 4ec8e35
import importlib.util,json,struct,tempfile,unittest
from pathlib import Path
from unittest.mock import patch
s=importlib.util.spec_from_file_location('page_check',Path(__file__).resolve().parents[1]/'tools/project-page-check.py');m=importlib.util.module_from_spec(s);s.loader.exec_module(m)
class PageChecks(unittest.TestCase):
 def setUp(self):
  self.tmp=tempfile.TemporaryDirectory();self.r=Path(self.tmp.name)
  g=json.dumps({'meshes':[{}],'nodes':[{}]}).encode();g+=b' '*((-len(g))%4)
  (self.r/'board.glb').write_bytes(struct.pack('<4sIIII',b'glTF',2,20+len(g),len(g),0x4e4f534a)+g)
  (self.r/'project.pdf').write_bytes(b'%PDF-test')
  self.p={'component':{'parts':{'model_3d':'board.glb','datasheet':'project.pdf'}},'sections':['hero','readme','viewers']}
  for i in range(6):(self.r/f'{i}.png').write_bytes(b'fixture')
  (self.r/'README.md').write_text('\n'.join(f'![Review {i}]({i}.png)' for i in range(6)))
  self.mock=patch.object(m.subprocess,'run',return_value=type('Result',(),{'stdout':'Readable project specification '*100})());self.mock.start()
 def tearDown(self):self.mock.stop();self.tmp.cleanup()
 def run_check(self):
  (self.r/'page.json').write_text(json.dumps(self.p));return m.check(self.r)
 def test_complete_structure_still_requires_visual_review(self):
  r=self.run_check();self.assertTrue(r['passed']);self.assertIn('pending',r['visualAcceptance'])
 def test_implicit_component_asset_is_rejected(self):
  self.p['component']['parts']={};self.assertFalse(self.run_check()['passed'])
 def test_datasheet_before_readme_rejected(self):
  self.p['sections']=['viewers','readme'];self.assertFalse(self.run_check()['passed'])
 def test_blank_pdf_rejected(self):
  m.subprocess.run.return_value.stdout='';self.assertFalse(self.run_check()['passed'])
 def test_duplicate_images_not_six_screenshots(self):
  (self.r/'README.md').write_text('![x](0.png)\n'*6);self.assertFalse(self.run_check()['passed'])
 def test_wrong_live_model_rejected(self):
  self.run_check();live=self.r/'live.json';live.write_text(json.dumps({'metadata':{'model_3d_path':'chip.glb','datasheet_path':'project.pdf'}}));self.assertFalse(m.check(self.r,live)['passed'])
 def test_molecule_page_needs_no_glb_and_suggests_dropping_one(self):
  # issue #137: the registry builds a molecule page's model; a bound GLB is a suggestion, never an error
  self.p['kind']='molecule';r=self.run_check();self.assertTrue(r['passed'],r);self.assertTrue(r['molecule'])
  self.assertTrue(any('model_3d' in x for x in r['suggestions']),r)
  self.p['component']['parts']={'datasheet':'project.pdf'};r=self.run_check();self.assertTrue(r['passed'],r);self.assertEqual(r['suggestions'],[])
  live=self.r/'live.json';live.write_text(json.dumps({'metadata':{'model_3d_path':'registry.glb','datasheet_path':'project.pdf'}}));self.assertTrue(m.check(self.r,live)['passed'])
if __name__=='__main__':unittest.main()